Merge mozilla-central to mozilla-inbound. a=merge CLOSED TREE

This commit is contained in:
Ciure Andrei 2018-08-29 01:31:20 +03:00
Родитель 7731ae9fb2 d716a04e20
Коммит 9aabc73223
204 изменённых файлов: 2466 добавлений и 2471 удалений

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

@ -35,6 +35,10 @@ browser/extensions/mortar/ppapi/.*
db/sqlite3/src/.*
devtools/client/sourceeditor/codemirror/.*
devtools/client/sourceeditor/tern/.*
dom/media/gmp/widevine-adapter/content_decryption_module.h
dom/media/gmp/widevine-adapter/content_decryption_module_export.h
dom/media/gmp/widevine-adapter/content_decryption_module_ext.h
dom/media/gmp/widevine-adapter/content_decryption_module_proxy.h
editor/libeditor/tests/browserscope/lib/richtext/.*
editor/libeditor/tests/browserscope/lib/richtext2/.*
extensions/spellcheck/hunspell/src/.*

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

@ -120,8 +120,6 @@ devtools/shared/qrcode/tests/mochitest/test_decode.html
devtools/shared/tests/mochitest/*.html
devtools/shared/webconsole/test/test_*.html
# Soon to be removed
devtools/client/commandline/**
# Soon to be removed, the new/ directory is explicitly excluded below due to
# also being an imported repository.
devtools/client/debugger/**
@ -136,6 +134,9 @@ devtools/client/webide/preferences/**
devtools/shared/preferences/**
devtools/startup/preferences/devtools-startup.js
# Ignore devtools generated code
devtools/shared/css/generated/properties-db.js
# Ignore devtools third-party libs
devtools/shared/jsbeautify/*
devtools/shared/acorn/*

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

@ -760,7 +760,7 @@ DocAccessibleParent::SendParentCOMProxy()
}
#if defined(MOZ_CONTENT_SANDBOX)
mParentProxyStream = std::move(holder.GetPreservedStream());
mParentProxyStream = holder.GetPreservedStream();
#endif // defined(MOZ_CONTENT_SANDBOX)
}

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

@ -247,7 +247,7 @@ HandlerProvider::BuildDynamicIA2Data(DynamicIA2Data* aOutIA2Data)
return FAILED(hr);
};
auto cleanup = [this, aOutIA2Data]() -> void {
auto cleanup = [aOutIA2Data]() -> void {
CleanupDynamicIA2Data(*aOutIA2Data);
};

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

@ -70,14 +70,14 @@ PlatformChild::PlatformChild()
UniquePtr<mozilla::mscom::RegisteredProxy> customProxy;
mozilla::mscom::EnsureMTA([&customProxy]() -> void {
customProxy = std::move(mozilla::mscom::RegisterProxy());
customProxy = mozilla::mscom::RegisterProxy();
});
mCustomProxy = std::move(customProxy);
// IA2 needs to be registered in both the main thread's STA as well as the MTA
UniquePtr<mozilla::mscom::RegisteredProxy> ia2ProxyMTA;
mozilla::mscom::EnsureMTA([&ia2ProxyMTA]() -> void {
ia2ProxyMTA = std::move(mozilla::mscom::RegisterProxy(L"ia2marshal.dll"));
ia2ProxyMTA = mozilla::mscom::RegisterProxy(L"ia2marshal.dll");
});
mIA2ProxyMTA = std::move(ia2ProxyMTA);
}

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

@ -244,7 +244,7 @@ ProxyAccessible::BoundsInCSSPixels()
}
nsIntRect rect;
HRESULT hr = custom->get_boundsInCSSPixels(&rect.x, &rect.y, &rect.width, &rect.height);
Unused << custom->get_boundsInCSSPixels(&rect.x, &rect.y, &rect.width, &rect.height);
return rect;
}
@ -416,7 +416,7 @@ ProxyAccessible::RelationByType(RelationType aType) const
}
CoTaskMemFree(targets);
return std::move(proxies);
return proxies;
}
double

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

@ -1770,7 +1770,7 @@ AccessibleWrap::InvalidateHandlers()
if (hr == CO_E_OBJNOTCONNECTED || hr == kErrorServerDied) {
sHandlerControllers->RemoveElement(controller);
} else {
NS_WARN_IF(FAILED(hr));
Unused << NS_WARN_IF(FAILED(hr));
}
}
}

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

@ -1737,10 +1737,6 @@ pref("app.shield.optoutstudies.enabled", true);
pref("app.shield.optoutstudies.enabled", false);
#endif
// Savant Shield study preferences
pref("shield.savant.enabled", false);
pref("shield.savant.loglevel", "warn");
// Multi-lingual preferences
pref("intl.multilingual.enabled", false);

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

@ -19,6 +19,7 @@ XPCOMUtils.defineLazyModuleGetters(this, {
BrowserUsageTelemetry: "resource:///modules/BrowserUsageTelemetry.jsm",
BrowserUtils: "resource://gre/modules/BrowserUtils.jsm",
BrowserWindowTracker: "resource:///modules/BrowserWindowTracker.jsm",
CFRPageActions: "resource://activity-stream/lib/CFRPageActions.jsm",
CharsetMenu: "resource://gre/modules/CharsetMenu.jsm",
Color: "resource://gre/modules/Color.jsm",
ContentSearch: "resource:///modules/ContentSearch.jsm",
@ -4817,6 +4818,8 @@ var XULBrowserWindow = {
CustomizationHandler.isCustomizing()) {
gCustomizeMode.exit();
}
CFRPageActions.updatePageActions(gBrowser.selectedBrowser);
}
UpdateBackForwardCommands(gBrowser.webNavigation);
ReaderParent.updateReaderButton(gBrowser.selectedBrowser);

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

@ -634,6 +634,16 @@ xmlns="http://www.w3.org/1999/xhtml"
accesskey="&syncSyncNowItem.accesskey;"
id="syncedTabsRefreshFilter"/>
</menupopup>
<hbox id="statuspanel" inactive="true" layer="true">
<hbox id="statuspanel-inner">
<label id="statuspanel-label"
role="status"
aria-live="off"
flex="1"
crop="end"/>
</hbox>
</hbox>
</popupset>
<box id="appMenu-viewCache" hidden="true"/>
@ -1261,37 +1271,7 @@ xmlns="http://www.w3.org/1999/xhtml"
<tabbox id="tabbrowser-tabbox"
flex="1" eventnode="document" tabcontainer="tabbrowser-tabs">
<tabpanels id="tabbrowser-tabpanels"
flex="1" class="plain" selectedIndex="0">
<notificationbox flex="1" notificationside="top">
<!-- Set large flex to allow the devtools toolbox to set a flex attribute.
We don't want the toolbox to actually take up free space, but we do want it to collapse when the window shrinks, and with flex=0 it can't.
When the toolbox is on the bottom it's a sibling of browserSidebarContainer,
and when it's on the side it's a sibling of browserContainer. -->
<hbox flex="10000" class="browserSidebarContainer">
<vbox flex="10000" class="browserContainer">
<stack flex="1" class="browserStack">
<browser id="tabbrowser-initialBrowser" type="content"
message="true" messagemanagergroup="browsers"
primary="true" blank="true"
tooltip="aHTMLTooltip"
contextmenu="contentAreaContextMenu"
autocompletepopup="PopupAutoComplete"
selectmenulist="ContentSelectDropdown"
datetimepicker="DateTimePickerPanel"/>
</stack>
<hbox id="statuspanel" inactive="true" layer="true">
<hbox id="statuspanel-inner">
<label id="statuspanel-label"
role="status"
aria-live="off"
flex="1"
crop="end"/>
</hbox>
</hbox>
</vbox>
</hbox>
</notificationbox>
</tabpanels>
flex="1" class="plain" selectedIndex="0"/>
</tabbox>
</vbox>
<vbox id="browser-border-end" hidden="true" layer="true"/>

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

@ -282,50 +282,40 @@ window._gBrowser = {
return this._selectedBrowser;
},
get initialBrowser() {
delete this.initialBrowser;
return this.initialBrowser = document.getElementById("tabbrowser-initialBrowser");
},
_setupInitialBrowserAndTab() {
let browser = this.initialBrowser;
this._selectedBrowser = browser;
browser.permanentKey = {};
// Bug 1362774 will adjust this to only set `uriIsAboutBlank` when
// necessary. For now, we always pass it.
let browser = this._createBrowser({uriIsAboutBlank: true});
browser.setAttribute("primary", "true");
browser.setAttribute("blank", "true");
browser.droppedLinkHandler = handleDroppedLink;
browser.loadURI = _loadURI.bind(null, browser);
let autoScrollPopup = browser._createAutoScrollPopup();
autoScrollPopup.id = "autoscroller";
document.getElementById("mainPopupSet").appendChild(autoScrollPopup);
browser.setAttribute("autoscrollpopup", autoScrollPopup.id);
this._defaultBrowserAttributes = {
autoscrollpopup: "",
contextmenu: "",
datetimepicker: "",
message: "",
messagemanagergroup: "",
selectmenulist: "",
tooltip: "",
type: "",
};
for (let attribute in this._defaultBrowserAttributes) {
this._defaultBrowserAttributes[attribute] = browser.getAttribute(attribute);
}
let uniqueId = this._generateUniquePanelID();
let notificationbox = this.getNotificationBox(browser);
notificationbox.id = uniqueId;
this.tabpanels.appendChild(notificationbox);
let tab = this.tabs[0];
this._selectedTab = tab;
let uniqueId = this._generateUniquePanelID();
this.tabpanels.children[0].id = uniqueId;
tab.linkedPanel = uniqueId;
this._selectedTab = tab;
this._selectedBrowser = browser;
tab.permanentKey = browser.permanentKey;
tab._tPos = 0;
tab._fullyOpen = true;
tab.linkedBrowser = browser;
this._tabForBrowser.set(browser, tab);
this._appendStatusPanel();
this.initialBrowser = browser;
let autoScrollPopup = browser._createAutoScrollPopup();
autoScrollPopup.id = "autoscroller";
document.getElementById("mainPopupSet").appendChild(autoScrollPopup);
browser.setAttribute("autoscrollpopup", autoScrollPopup.id);
this._autoScrollPopup = autoScrollPopup;
// Hook the browser up with a progress listener.
let tabListener = new TabProgressListener(tab, browser, true, false);
let filter = Cc["@mozilla.org/appshell/component/browser-status-filter;1"]
@ -1014,7 +1004,6 @@ window._gBrowser = {
}
});
newTab.dispatchEvent(event);
Services.telemetry.recordEvent("savant", "tab", "select", null, { subcategory: "frame" });
this._tabAttrModified(oldTab, ["selected"]);
this._tabAttrModified(newTab, ["selected"]);
@ -1812,8 +1801,17 @@ window._gBrowser = {
let b = document.createXULElement("browser");
b.permanentKey = {};
for (let attribute in this._defaultBrowserAttributes) {
b.setAttribute(attribute, this._defaultBrowserAttributes[attribute]);
const defaultBrowserAttributes = {
contextmenu: "contentAreaContextMenu",
datetimepicker: "DateTimePickerPanel",
message: "true",
messagemanagergroup: "browsers",
selectmenulist: "ContentSelectDropdown",
tooltip: "aHTMLTooltip",
type: "content",
};
for (let attribute in defaultBrowserAttributes) {
b.setAttribute(attribute, defaultBrowserAttributes[attribute]);
}
if (userContextId) {
@ -1843,6 +1841,10 @@ window._gBrowser = {
if (!isPreloadBrowser) {
b.setAttribute("autocompletepopup", "PopupAutoComplete");
}
if (this._autoScrollPopup) {
b.setAttribute("autoscrollpopup", this._autoScrollPopup.id);
}
/*
* This attribute is meant to describe if the browser is the
@ -1889,13 +1891,17 @@ window._gBrowser = {
stack.appendChild(b);
stack.setAttribute("flex", "1");
// Create the browserContainer
// We set large flex on both containers to allow the devtools toolbox to
// set a flex attribute. We don't want the toolbox to actually take up free
// space, but we do want it to collapse when the window shrinks, and with
// flex=0 it can't. When the toolbox is on the bottom it's a sibling of
// browserSidebarContainer, and when it's on the side it's a sibling of
// browserContainer.
let browserContainer = document.createXULElement("vbox");
browserContainer.className = "browserContainer";
browserContainer.appendChild(stack);
browserContainer.setAttribute("flex", "10000");
// Create the sidebar container
let browserSidebarContainer = document.createXULElement("hbox");
browserSidebarContainer.className = "browserSidebarContainer";
browserSidebarContainer.appendChild(browserContainer);
@ -2472,7 +2478,6 @@ window._gBrowser = {
// even if the event listener opens or closes tabs.
let evt = new CustomEvent("TabOpen", { bubbles: true, detail: eventDetail || {} });
t.dispatchEvent(evt);
Services.telemetry.recordEvent("savant", "tab", "open", null, { subcategory: "frame" });
if (!usingPreloadedContent && originPrincipal && aURI) {
let { URI_INHERITS_SECURITY_CONTEXT } = Ci.nsIProtocolHandler;
@ -2886,7 +2891,6 @@ window._gBrowser = {
// inspect the tab that's about to close.
var evt = new CustomEvent("TabClose", { bubbles: true, detail: { adoptedBy: aAdoptedByTab } });
aTab.dispatchEvent(evt);
Services.telemetry.recordEvent("savant", "tab", "close", null, { subcategory: "frame" });
if (this.tabs.length == 2) {
// We're closing one of our two open tabs, inform the other tab that its

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

@ -1 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><path fill="context-fill" d="M14.5 8c-.971 0-1 1-1.75 1a.765.765 0 0 1-.75-.75V5a1 1 0 0 0-1-1H7.75A.765.765 0 0 1 7 3.25c0-.75 1-.779 1-1.75C8 .635 7.1 0 6 0S4 .635 4 1.5c0 .971 1 1 1 1.75a.765.765 0 0 1-.75.75H1a1 1 0 0 0-1 1v2.25A.765.765 0 0 0 .75 8c.75 0 .779-1 1.75-1C3.365 7 4 7.9 4 9s-.635 2-1.5 2c-.971 0-1-1-1.75-1a.765.765 0 0 0-.75.75V15a1 1 0 0 0 1 1h3.25a.765.765 0 0 0 .75-.75c0-.75-1-.779-1-1.75 0-.865.9-1.5 2-1.5s2 .635 2 1.5c0 .971-1 1-1 1.75a.765.765 0 0 0 .75.75H11a1 1 0 0 0 1-1v-3.25a.765.765 0 0 1 .75-.75c.75 0 .779 1 1.75 1 .865 0 1.5-.9 1.5-2s-.635-2-1.5-2z"/></svg>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><path fill="context-fill" fill-opacity="context-fill-opacity" d="M14.5 8c-.971 0-1 1-1.75 1a.765.765 0 0 1-.75-.75V5a1 1 0 0 0-1-1H7.75A.765.765 0 0 1 7 3.25c0-.75 1-.779 1-1.75C8 .635 7.1 0 6 0S4 .635 4 1.5c0 .971 1 1 1 1.75a.765.765 0 0 1-.75.75H1a1 1 0 0 0-1 1v2.25A.765.765 0 0 0 .75 8c.75 0 .779-1 1.75-1C3.365 7 4 7.9 4 9s-.635 2-1.5 2c-.971 0-1-1-1.75-1a.765.765 0 0 0-.75.75V15a1 1 0 0 0 1 1h3.25a.765.765 0 0 0 .75-.75c0-.75-1-.779-1-1.75 0-.865.9-1.5 2-1.5s2 .635 2 1.5c0 .971-1 1-1 1.75a.765.765 0 0 0 .75.75H11a1 1 0 0 0 1-1v-3.25a.765.765 0 0 1 .75-.75c.75 0 .779 1 1.75 1 .865 0 1.5-.9 1.5-2s-.635-2-1.5-2z"/></svg>

До

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

После

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

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

@ -0,0 +1,247 @@
/* 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 POPUP_NOTIFICATION_ID = "contextual-feature-recommendation";
const DELAY_BEFORE_EXPAND_MS = 1000;
const DURATION_OF_EXPAND_MS = 5000;
/**
* A WeakMap from browsers to {host, recommendation} pairs. Recommendations are
* defined in the ExtensionDoorhanger.schema.json.
*
* A recommendation is specific to a browser and host and is active until the
* given browser is closed or the user navigates (within that browser) away from
* the host.
*/
const RecommendationMap = new WeakMap();
/**
* A WeakMap from windows to their CFR PageAction.
*/
const PageActionMap = new WeakMap();
/**
* We need one PageAction for each window
*/
class PageAction {
constructor(win, dispatchToASRouter) {
this.window = win;
this.urlbar = win.document.getElementById("urlbar");
this.container = win.document.getElementById("contextual-feature-recommendation");
this.button = win.document.getElementById("cfr-button");
this.label = win.document.getElementById("cfr-label");
this._dispatchToASRouter = dispatchToASRouter;
this._popupStateChange = this._popupStateChange.bind(this);
this._collapse = this._collapse.bind(this);
this._handleClick = this._handleClick.bind(this);
// Saved timeout IDs for scheduled state changes, so they can be cancelled
this.stateTransitionTimeoutIDs = [];
this.container.onclick = this._handleClick;
}
async show(notificationText, shouldExpand = false) {
this.container.hidden = false;
this.label.value = notificationText;
// Wait for layout to flush to avoid a synchronous reflow then calculate the
// label width. We can safely get the width even though the recommendation is
// collapsed; the label itself remains full width (with its overflow hidden)
await this.window.promiseDocumentFlushed;
const [{width}] = this.label.getClientRects();
this.urlbar.style.setProperty("--cfr-label-width", `${width}px`);
if (shouldExpand) {
this._clearScheduledStateChanges();
// After one second, expand
this._expand(DELAY_BEFORE_EXPAND_MS);
// Five seconds later, collapse again
this._collapse(DELAY_BEFORE_EXPAND_MS + DURATION_OF_EXPAND_MS);
}
}
hide() {
this.container.hidden = true;
this._clearScheduledStateChanges();
this.urlbar.removeAttribute("cfr-recommendation-state");
}
_expand(delay = 0) {
if (!delay) {
// Non-delayed state change overrides any scheduled state changes
this._clearScheduledStateChanges();
this.urlbar.setAttribute("cfr-recommendation-state", "expanded");
} else {
this.stateTransitionTimeoutIDs.push(this.window.setTimeout(() => {
this.urlbar.setAttribute("cfr-recommendation-state", "expanded");
}, delay));
}
}
_collapse(delay = 0) {
if (!delay) {
// Non-delayed state change overrides any scheduled state changes
this._clearScheduledStateChanges();
if (this.urlbar.getAttribute("cfr-recommendation-state") === "expanded") {
this.urlbar.setAttribute("cfr-recommendation-state", "collapsed");
}
} else {
this.stateTransitionTimeoutIDs.push(this.window.setTimeout(() => {
if (this.urlbar.getAttribute("cfr-recommendation-state") === "expanded") {
this.urlbar.setAttribute("cfr-recommendation-state", "collapsed");
}
}, delay));
}
}
_clearScheduledStateChanges() {
while (this.stateTransitionTimeoutIDs.length > 0) {
// clearTimeout is safe even with invalid/expired IDs
this.window.clearTimeout(this.stateTransitionTimeoutIDs.pop());
}
}
// This is called when the popup closes as a result of interaction _outside_
// the popup, e.g. by hitting <esc>
_popupStateChange(state) {
if (["dismissed", "removed"].includes(state)) {
this._collapse();
}
}
/**
* Respond to a user click on the recommendation by showing a doorhanger/
* popup notification
*/
_handleClick(event) {
const browser = this.window.gBrowser.selectedBrowser;
if (!RecommendationMap.has(browser)) {
// There's no recommendation for this browser, so the user shouldn't have
// been able to click
this.hide();
return;
}
const {content} = RecommendationMap.get(browser);
// The recommendation should remain either collapsed or expanded while the
// doorhanger is showing
this._clearScheduledStateChanges();
// A hacky way of setting the popup anchor outside the usual url bar icon box
// See https://searchfox.org/mozilla-central/rev/847b64cc28b74b44c379f9bff4f415b97da1c6d7/toolkit/modules/PopupNotifications.jsm#42
browser.cfrpopupnotificationanchor = this.container;
const {primary, secondary} = content.buttons;
const mainAction = {
label: primary.label,
accessKey: primary.accessKey,
callback: () => this._dispatchToASRouter(primary.action)
};
const secondaryActions = [{
label: secondary.label,
accessKey: secondary.accessKey,
callback: this._collapse
}];
const options = {
popupIconURL: content.addon.icon,
hideClose: true,
eventCallback: this._popupStateChange
};
this.window.PopupNotifications.show(
browser,
POPUP_NOTIFICATION_ID,
content.text,
"cfr",
mainAction,
secondaryActions,
options
);
}
}
function isHostMatch(browser, host) {
return (browser.documentURI.scheme.startsWith("http") &&
browser.documentURI.host === host);
}
const CFRPageActions = {
// For testing purposes
RecommendationMap,
PageActionMap,
/**
* To be called from browser.js on a location change, passing in the browser
* that's been updated
*/
updatePageActions(browser) {
const win = browser.ownerGlobal;
const pageAction = PageActionMap.get(win);
if (!pageAction || browser !== win.gBrowser.selectedBrowser) {
return;
}
if (RecommendationMap.has(browser)) {
const {host, content} = RecommendationMap.get(browser);
if (isHostMatch(browser, host)) {
// The browser has a recommendation specified with this host, so show
// the page action
pageAction.show(content.notification_text);
} else {
// The user has navigated away from the specified host in the given
// browser, so the recommendation is no longer valid and should be removed
RecommendationMap.delete(browser);
pageAction.hide();
}
} else {
// There's no recommendation specified for this browser, so hide the page action
pageAction.hide();
}
},
/**
* Add a recommendation specific to the given browser and host.
* @param browser The browser for the recommendation
* @param host The host for the recommendation
* @param recommendation The recommendation to show
* @param dispatchToASRouter A function to dispatch resulting actions to
* @param force Force the recommendation to appear if the host doesn't match
* @return Did adding the recommendation succeed?
*/
async addRecommendation(browser, host, recommendation, dispatchToASRouter, force = false) {
const win = browser.ownerGlobal;
if (browser !== win.gBrowser.selectedBrowser || !(force || isHostMatch(browser, host))) {
return false;
}
const {id, content} = recommendation;
RecommendationMap.set(browser, {id, host, content});
if (!PageActionMap.has(win)) {
PageActionMap.set(win, new PageAction(win, dispatchToASRouter));
}
await PageActionMap.get(win).show(recommendation.content.notification_text, true);
return true;
},
/**
* Clear all recommendations and hide all PageActions
*/
clearRecommendations() {
for (const [win, pageAction] of PageActionMap) {
pageAction.hide();
PageActionMap.delete(win);
}
RecommendationMap.clear();
}
};
const EXPORTED_SYMBOLS = ["CFRPageActions"];

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

@ -429,7 +429,6 @@ XPCOMUtils.defineLazyModuleGetters(this, {
RemoteSettings: "resource://services-settings/remote-settings.js",
SafeBrowsing: "resource://gre/modules/SafeBrowsing.jsm",
Sanitizer: "resource:///modules/Sanitizer.jsm",
SavantShieldStudy: "resource:///modules/SavantShieldStudy.jsm",
SessionStartup: "resource:///modules/sessionstore/SessionStartup.jsm",
SessionStore: "resource:///modules/sessionstore/SessionStore.jsm",
ShellService: "resource:///modules/ShellService.jsm",
@ -525,9 +524,6 @@ const listeners = {
"RemoteLogins:autoCompleteLogins": ["LoginManagerParent"],
"RemoteLogins:removeLogin": ["LoginManagerParent"],
"RemoteLogins:insecureLoginFormPresent": ["LoginManagerParent"],
// For Savant Shield study, bug 1465685. Study on desktop only.
"LoginStats:LoginFillSuccessful": ["LoginManagerParent"],
"LoginStats:LoginEncountered": ["LoginManagerParent"],
// PLEASE KEEP THIS LIST IN SYNC WITH THE MOBILE LISTENERS IN BrowserCLH.js
"WCCR:registerProtocolHandler": ["Feeds"],
"rtcpeer:CancelRequest": ["webrtcUI"],
@ -1400,8 +1396,6 @@ BrowserGlue.prototype = {
}
Normandy.uninit();
SavantShieldStudy.uninit();
},
// All initial windows have opened.
@ -1558,10 +1552,6 @@ BrowserGlue.prototype = {
Services.tm.idleDispatchToMainThread(() => {
Blocklist.loadBlocklistAsync();
});
Services.tm.idleDispatchToMainThread(() => {
SavantShieldStudy.init();
});
},
/**

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

@ -7,18 +7,29 @@
ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
ChromeUtils.import("resource://services-sync/SyncedTabs.jsm");
ChromeUtils.import("resource:///modules/syncedtabs/SyncedTabsDeckComponent.js");
ChromeUtils.import("resource:///actors/LightweightThemeChild.jsm");
ChromeUtils.defineModuleGetter(this, "fxAccounts",
"resource://gre/modules/FxAccounts.jsm");
var syncedTabsDeckComponent = new SyncedTabsDeckComponent({window, SyncedTabs, fxAccounts});
let themeListener;
let onLoaded = () => {
themeListener = new LightweightThemeChild({
content: window,
chromeOuterWindowID: window.top.windowUtils.outerWindowID,
docShell: window.docShell,
});
syncedTabsDeckComponent.init();
document.getElementById("template-container").appendChild(syncedTabsDeckComponent.container);
};
let onUnloaded = () => {
if (themeListener) {
themeListener.cleanup();
}
removeEventListener("DOMContentLoaded", onLoaded);
removeEventListener("unload", onUnloaded);
syncedTabsDeckComponent.uninit();

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

@ -19,6 +19,7 @@
<head>
<script src="chrome://browser/content/syncedtabs/sidebar.js" type="application/javascript"></script>
<script type="application/javascript" src="chrome://browser/content/utilityOverlay.js"/>
<script src="chrome://browser/content/contentTheme.js"></script>
<link rel="stylesheet" type="text/css" media="all" href="chrome://browser/skin/syncedtabs/sidebar.css"/>
<link rel="stylesheet" type="text/css" media="all" href="chrome://global/skin/"/>

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

@ -75,7 +75,7 @@ var Translation = {
openProviderAttribution() {
let attribution = this.supportedEngines[this.translationEngine];
ChromeUtils.import("resource:///modules/BrowserWindowTracker.jsm");
BrowserWindowTracker.getTopWindow().openUILinkIn(attribution, "tab");
BrowserWindowTracker.getTopWindow().openWebLinkIn(attribution, "tab");
},
/**

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

@ -366,10 +366,6 @@ let urlbarListener = {
Services.telemetry
.getKeyedHistogramById("FX_URLBAR_SELECTED_RESULT_INDEX_BY_TYPE")
.add(actionType, idx);
if (actionType === "bookmark" || actionType === "history") {
Services.telemetry.recordEvent("savant", "follow_urlbar_link", actionType, null,
{ subcategory: "navigation" });
}
} else {
Cu.reportError("Unknown FX_URLBAR_SELECTED_RESULT_TYPE type: " +
actionType);
@ -503,9 +499,6 @@ let BrowserUsageTelemetry = {
scalarKey, 1);
Services.telemetry.recordEvent("navigation", "search", source, action,
{ engine: getSearchEngineId(engine) });
Services.telemetry.recordEvent("savant", "search", source, action,
{ subcategory: "navigation",
engine: getSearchEngineId(engine) });
},
_handleSearchAction(engine, source, details) {

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

@ -1,530 +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/. */
/* eslint semi: error */
"use strict";
var EXPORTED_SYMBOLS = ["SavantShieldStudy"];
ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
ChromeUtils.import("resource://gre/modules/Services.jsm");
XPCOMUtils.defineLazyModuleGetters(this, {
AddonManager: "resource://gre/modules/AddonManager.jsm",
PlacesUtils: "resource://gre/modules/PlacesUtils.jsm"
});
// See LOG_LEVELS in Console.jsm. Examples: "all", "info", "warn", & "error".
const PREF_LOG_LEVEL = "shield.savant.loglevel";
// Create a new instance of the ConsoleAPI so we can control the maxLogLevel with a pref.
XPCOMUtils.defineLazyGetter(this, "log", () => {
let ConsoleAPI = ChromeUtils.import("resource://gre/modules/Console.jsm", {}).ConsoleAPI;
let consoleOptions = {
maxLogLevelPref: PREF_LOG_LEVEL,
prefix: "SavantShieldStudy",
};
return new ConsoleAPI(consoleOptions);
});
class SavantShieldStudyClass {
constructor() {
this.STUDY_PREF = "shield.savant.enabled";
this.STUDY_TELEMETRY_CATEGORY = "savant";
this.ALWAYS_PRIVATE_BROWSING_PREF = "browser.privatebrowsing.autostart";
this.STUDY_DURATION_OVERRIDE_PREF = "shield.savant.duration_override";
this.STUDY_EXPIRATION_DATE_PREF = "shield.savant.expiration_date";
// ms = 'x' weeks * 7 days/week * 24 hours/day * 60 minutes/hour
// * 60 seconds/minute * 1000 milliseconds/second
this.DEFAULT_STUDY_DURATION_MS = 4 * 7 * 24 * 60 * 60 * 1000;
// If on startupStudy(), user is ineligible or study has expired,
// no probe listeners from this module have been added yet
this.shouldRemoveListeners = true;
}
init() {
this.telemetryEvents = new TelemetryEvents(this.STUDY_TELEMETRY_CATEGORY);
this.addonListener = new AddonListener(this.STUDY_TELEMETRY_CATEGORY);
this.bookmarkObserver = new BookmarkObserver(this.STUDY_TELEMETRY_CATEGORY);
this.menuListener = new MenuListener(this.STUDY_TELEMETRY_CATEGORY);
// check the pref in case Normandy flipped it on before we could add the pref listener
this.shouldCollect = Services.prefs.getBoolPref(this.STUDY_PREF);
if (this.shouldCollect) {
this.startupStudy();
}
Services.prefs.addObserver(this.STUDY_PREF, this);
}
observe(subject, topic, data) {
if (topic === "nsPref:changed" && data === this.STUDY_PREF) {
// toggle state of the pref
this.shouldCollect = !this.shouldCollect;
if (this.shouldCollect) {
this.startupStudy();
} else {
// The pref has been turned off
this.endStudy("study_disable");
}
}
}
startupStudy() {
// enable before any possible calls to endStudy, since it sends an 'end_study' event
this.telemetryEvents.enableCollection();
if (!this.isEligible()) {
this.shouldRemoveListeners = false;
this.endStudy("ineligible");
return;
}
this.initStudyDuration();
if (this.isStudyExpired()) {
log.debug("Study expired in between this and the previous session.");
this.shouldRemoveListeners = false;
this.endStudy("expired");
}
this.addonListener.init();
this.bookmarkObserver.init();
this.menuListener.init();
}
isEligible() {
const isAlwaysPrivateBrowsing = Services.prefs.getBoolPref(this.ALWAYS_PRIVATE_BROWSING_PREF);
if (isAlwaysPrivateBrowsing) {
return false;
}
return true;
}
initStudyDuration() {
if (Services.prefs.getStringPref(this.STUDY_EXPIRATION_DATE_PREF, "")) {
return;
}
Services.prefs.setStringPref(
this.STUDY_EXPIRATION_DATE_PREF,
this.getExpirationDateString()
);
}
getDurationFromPref() {
return Services.prefs.getIntPref(this.STUDY_DURATION_OVERRIDE_PREF, 0);
}
getExpirationDateString() {
const now = Date.now();
const studyDurationInMs =
this.getDurationFromPref()
|| this.DEFAULT_STUDY_DURATION_MS;
const expirationDateInt = now + studyDurationInMs;
return new Date(expirationDateInt).toISOString();
}
isStudyExpired() {
const expirationDateInt =
Date.parse(Services.prefs.getStringPref(
this.STUDY_EXPIRATION_DATE_PREF,
this.getExpirationDateString()
));
if (isNaN(expirationDateInt)) {
log.error(
`The value for the preference ${this.STUDY_EXPIRATION_DATE_PREF} is invalid.`
);
return false;
}
if (Date.now() > expirationDateInt) {
return true;
}
return false;
}
endStudy(reason) {
log.debug(`Ending the study due to reason: ${ reason }`);
const isStudyEnding = true;
Services.telemetry.recordEvent(this.STUDY_TELEMETRY_CATEGORY, "end_study", reason, null,
{ subcategory: "shield" });
this.telemetryEvents.disableCollection();
this.uninit(isStudyEnding);
// These prefs needs to persist between restarts, so only reset on endStudy
Services.prefs.clearUserPref(this.STUDY_PREF);
Services.prefs.clearUserPref(this.STUDY_EXPIRATION_DATE_PREF);
}
// Called on every Firefox shutdown and endStudy
uninit(isStudyEnding = false) {
// if just shutting down, check for expiration, so the endStudy event can
// be sent along with this session's main ping.
if (!isStudyEnding && this.isStudyExpired()) {
log.debug("Study expired during this session.");
this.endStudy("expired");
return;
}
this.addonListener.uninit();
this.bookmarkObserver.uninit();
this.menuListener.uninit();
Services.prefs.removeObserver(this.ALWAYS_PRIVATE_BROWSING_PREF, this);
Services.prefs.removeObserver(this.STUDY_PREF, this);
Services.prefs.removeObserver(this.STUDY_DURATION_OVERRIDE_PREF, this);
Services.prefs.clearUserPref(PREF_LOG_LEVEL);
Services.prefs.clearUserPref(this.STUDY_DURATION_OVERRIDE_PREF);
}
}
// References:
// - https://hg.mozilla.org/mozilla-central/file/tip/toolkit/components/normandy/lib/TelemetryEvents.jsm
// - https://hg.mozilla.org/mozilla-central/file/tip/toolkit/components/normandy/lib/PreferenceExperiments.jsm#l357
class TelemetryEvents {
constructor(studyCategory) {
this.STUDY_TELEMETRY_CATEGORY = studyCategory;
}
enableCollection() {
log.debug("Study has been enabled; turning ON data collection.");
Services.telemetry.setEventRecordingEnabled(this.STUDY_TELEMETRY_CATEGORY, true);
}
disableCollection() {
log.debug("Study has been disabled; turning OFF data collection.");
Services.telemetry.setEventRecordingEnabled(this.STUDY_TELEMETRY_CATEGORY, false);
}
}
class AddonListener {
constructor(studyCategory) {
this.STUDY_TELEMETRY_CATEGORY = studyCategory;
this.METHOD = "addon";
this.EXTRA_SUBCATEGORY = "customize";
}
init() {
this.listener = {
onInstalling: (addon, needsRestart) => {
const addon_id = addon.id;
this.recordEvent("install_start", addon_id);
},
onInstalled: (addon) => {
const addon_id = addon.id;
this.recordEvent("install_finish", addon_id);
},
onEnabled: (addon) => {
const addon_id = addon.id;
this.recordEvent("enable", addon_id);
},
onDisabled: (addon) => {
const addon_id = addon.id;
this.recordEvent("disable", addon_id);
},
onUninstalling: (addon, needsRestart) => {
const addon_id = addon.id;
this.recordEvent("remove_start", addon_id);
},
onUninstalled: (addon) => {
const addon_id = addon.id;
this.recordEvent("remove_finish", addon_id);
}
};
this.addListeners();
}
addListeners() {
AddonManager.addAddonListener(this.listener);
}
recordEvent(event, addon_id) {
log.debug(`Addon ID: ${addon_id}; event: ${ event }`);
Services.telemetry.recordEvent(this.STUDY_TELEMETRY_CATEGORY,
this.METHOD,
event,
addon_id,
{ subcategory: this.EXTRA_SUBCATEGORY });
}
removeListeners() {
AddonManager.removeAddonListener(this.listener);
}
uninit() {
if (SavantShieldStudy.shouldRemoveListeners) {
this.removeListeners();
}
}
}
class BookmarkObserver {
constructor(studyCategory) {
this.STUDY_TELEMETRY_CATEGORY = studyCategory;
// there are two probes: bookmark and follow_bookmark
this.METHOD_1 = "bookmark";
this.EXTRA_SUBCATEGORY_1 = "feature";
this.METHOD_2 = "follow_bookmark";
this.EXTRA_SUBCATEGORY_2 = "navigation";
this.TYPE_BOOKMARK = Ci.nsINavBookmarksService.TYPE_BOOKMARK;
// Ignore "fake" bookmarks created for bookmark tags
this.skipTags = true;
}
init() {
this.addObservers();
}
addObservers() {
PlacesUtils.bookmarks.addObserver(this);
}
onItemAdded(itemID, parentID, index, itemType, uri, title, dateAdded, guid, parentGUID, source) {
this.handleItemAddRemove(itemType, uri, source, "save");
}
onItemRemoved(itemID, parentID, index, itemType, uri, guid, parentGUID, source) {
this.handleItemAddRemove(itemType, uri, source, "remove");
}
handleItemAddRemove(itemType, uri, source, event) {
/*
* "place:query" uris are used to create containers like Most Visited or
* Recently Bookmarked. These are added as default bookmarks.
*/
if (itemType === this.TYPE_BOOKMARK && !uri.schemeIs("place")
&& source === PlacesUtils.bookmarks.SOURCE_DEFAULT) {
const isBookmarkProbe = true;
this.recordEvent(event, isBookmarkProbe);
}
}
// This observer is only fired for TYPE_BOOKMARK items.
onItemVisited(itemID, visitID, time, transitionType, uri, parentID, guid, parentGUID) {
const isBookmarkProbe = false;
this.recordEvent("open", isBookmarkProbe);
}
recordEvent(event, isBookmarkProbe) {
const method = isBookmarkProbe ? this.METHOD_1 : this.METHOD_2;
const subcategory = isBookmarkProbe ? this.EXTRA_SUBCATEGORY_1 : this.EXTRA_SUBCATEGORY_2;
Services.telemetry.recordEvent(this.STUDY_TELEMETRY_CATEGORY, method, event, null,
{
subcategory
});
}
removeObservers() {
PlacesUtils.bookmarks.removeObserver(this);
}
uninit() {
if (SavantShieldStudy.shouldRemoveListeners) {
this.removeObservers();
}
}
}
class MenuListener {
constructor(studyCategory) {
this.STUDY_TELEMETRY_CATEGORY = studyCategory;
this.NAVIGATOR_TOOLBOX_ID = "navigator-toolbox";
this.OVERFLOW_PANEL_ID = "widget-overflow";
this.LIBRARY_PANELVIEW_ID = "appMenu-libraryView";
this.HAMBURGER_PANEL_ID = "appMenu-popup";
this.DOTDOTDOT_PANEL_ID = "pageActionPanel";
this.windowWatcher = new WindowWatcher();
}
init() {
this.windowWatcher.init(this.loadIntoWindow.bind(this),
this.unloadFromWindow.bind(this),
this.onWindowError.bind(this));
}
loadIntoWindow(win) {
this.addListeners(win);
}
unloadFromWindow(win) {
this.removeListeners(win);
}
onWindowError(msg) {
log.error(msg);
}
addListeners(win) {
const doc = win.document;
const navToolbox = doc.getElementById(this.NAVIGATOR_TOOLBOX_ID);
const overflowPanel = doc.getElementById(this.OVERFLOW_PANEL_ID);
const hamburgerPanel = doc.getElementById(this.HAMBURGER_PANEL_ID);
const dotdotdotPanel = doc.getElementById(this.DOTDOTDOT_PANEL_ID);
/*
* the library menu "ViewShowing" event bubbles up on the navToolbox in its
* default location. A separate listener is needed if it is moved to the
* overflow panel via Hamburger > Customize
*/
navToolbox.addEventListener("ViewShowing", this);
overflowPanel.addEventListener("ViewShowing", this);
hamburgerPanel.addEventListener("popupshown", this);
dotdotdotPanel.addEventListener("popupshown", this);
}
handleEvent(evt) {
switch (evt.type) {
case "ViewShowing":
if (evt.target.id === this.LIBRARY_PANELVIEW_ID) {
log.debug("Library panel opened.");
this.recordEvent("library_menu");
}
break;
case "popupshown":
switch (evt.target.id) {
case this.HAMBURGER_PANEL_ID:
log.debug("Hamburger panel opened.");
this.recordEvent("hamburger_menu");
break;
case this.DOTDOTDOT_PANEL_ID:
log.debug("Dotdotdot panel opened.");
this.recordEvent("dotdotdot_menu");
break;
default:
break;
}
break;
}
}
recordEvent(method) {
Services.telemetry.recordEvent(this.STUDY_TELEMETRY_CATEGORY, method, "open", null,
{ subcategory: "menu" });
}
removeListeners(win) {
const doc = win.document;
const navToolbox = doc.getElementById(this.NAVIGATOR_TOOLBOX_ID);
const overflowPanel = doc.getElementById(this.OVERFLOW_PANEL_ID);
const hamburgerPanel = doc.getElementById(this.HAMBURGER_PANEL_ID);
const dotdotdotPanel = doc.getElementById(this.DOTDOTDOT_PANEL_ID);
try {
navToolbox.removeEventListener("ViewShowing", this);
overflowPanel.removeEventListener("ViewShowing", this);
hamburgerPanel.removeEventListener("popupshown", this);
dotdotdotPanel.removeEventListener("popupshown", this);
} catch (err) {
// Firefox is shutting down; elements have already been removed.
}
}
uninit() {
if (SavantShieldStudy.shouldRemoveListeners) {
this.windowWatcher.uninit();
}
}
}
/*
* The WindowWatcher is used to add/remove listeners from MenuListener
* to/from all windows.
*/
class WindowWatcher {
constructor() {
this._isActive = false;
this._loadCallback = null;
this._unloadCallback = null;
this._errorCallback = null;
}
// It is expected that loadCallback, unloadCallback, and errorCallback are bound
// to a `this` value.
init(loadCallback, unloadCallback, errorCallback) {
if (this._isActive) {
errorCallback("Called init, but WindowWatcher was already running");
return;
}
this._isActive = true;
this._loadCallback = loadCallback;
this._unloadCallback = unloadCallback;
this._errorCallback = errorCallback;
// Add loadCallback to existing windows
for (const win of Services.wm.getEnumerator("navigator:browser")) {
try {
this._loadCallback(win);
} catch (ex) {
this._errorCallback(`WindowWatcher code loading callback failed: ${ ex }`);
}
}
// Add loadCallback to future windows
// This will call the observe method on WindowWatcher
Services.ww.registerNotification(this);
}
uninit() {
if (!this._isActive) {
this._errorCallback("Called uninit, but WindowWatcher was already uninited");
return;
}
for (const win of Services.wm.getEnumerator("navigator:browser")) {
try {
this._unloadCallback(win);
} catch (ex) {
this._errorCallback(`WindowWatcher code unloading callback failed: ${ ex }`);
}
}
Services.ww.unregisterNotification(this);
this._loadCallback = null;
this._unloadCallback = null;
this._errorCallback = null;
this._isActive = false;
}
observe(win, topic) {
switch (topic) {
case "domwindowopened":
this._onWindowOpened(win);
break;
case "domwindowclosed":
this._onWindowClosed(win);
break;
default:
break;
}
}
_onWindowOpened(win) {
win.addEventListener("load", this, { once: true });
}
// only one event type expected: "load"
handleEvent(evt) {
const win = evt.target.ownerGlobal;
// make sure we only add window listeners to a DOMWindow (browser.xul)
const winType = win.document.documentElement.getAttribute("windowtype");
if (winType === "navigator:browser") {
this._loadCallback(win);
}
}
_onWindowClosed(win) {
this._unloadCallback(win);
}
}
const SavantShieldStudy = new SavantShieldStudyClass();

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

@ -138,7 +138,6 @@ EXTRA_JS_MODULES += [
'ReaderParent.jsm',
'RemotePrompt.jsm',
'Sanitizer.jsm',
'SavantShieldStudy.jsm',
'SchedulePressure.jsm',
'SiteDataManager.jsm',
'SitePermissions.jsm',

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

@ -6,7 +6,7 @@
/* These styles are intended to mimic XUL trees and the XUL search box. */
.content-container {
body:not([lwt-sidebar]) .content-container {
-moz-appearance: -moz-mac-source-list;
-moz-font-smoothing-background-color: -moz-mac-source-list;
}

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

@ -871,6 +871,10 @@ panelview .toolbarbutton-1,
outline: 0;
}
.subviewbutton[disabled="true"] {
color: var(--panel-disabled-color);
}
.subviewbutton > .toolbarbutton-text {
padding: 0;
padding-inline-start: 24px; /* This is 16px for the icon + 8px for the padding as defined below. */

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

@ -69,7 +69,6 @@ body {
white-space: nowrap;
overflow: hidden;
outline: none;
color: -moz-FieldText;
}
.item.selected > .item-title-container {
@ -296,3 +295,26 @@ body {
.filtered .textbox-search-icons .textbox-search-clear {
display: block;
}
/* Themed sidebars */
body[lwt-sidebar] {
background-color: var(--lwt-sidebar-background-color);
color: var(--lwt-sidebar-text-color);
}
body[lwt-sidebar] .item.selected > .item-title-container {
background-color: hsla(0,0%,80%,.3);
color: inherit;
}
body[lwt-sidebar-brighttext] .item.selected > .item-title-container {
-moz-appearance: none;
background-color: rgba(249,249,250,.1);
}
body[lwt-sidebar-highlight] .item.selected:focus > .item-title-container {
-moz-appearance: none;
background-color: var(--lwt-sidebar-highlight-background-color);
color: var(--lwt-sidebar-highlight-text-color);
}

6
config/external/moz.build поставляемый
Просмотреть файл

@ -8,12 +8,16 @@ external_dirs = []
DIRS += [
'lgpllibs',
'prio',
'sqlite',
]
if not CONFIG['MOZ_SYSTEM_JPEG']:
external_dirs += ['media/libjpeg']
if CONFIG['MOZ_LIBPRIO']:
DIRS += [
'prio',
]
# There's no "native" brotli or woff2 yet, but probably in the future...
external_dirs += ['modules/brotli']
external_dirs += ['modules/woff2']

3
config/external/msgpack/moz.build поставляемый
Просмотреть файл

@ -4,5 +4,6 @@
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
DIRS += ['/third_party/msgpack']
if CONFIG['MOZ_LIBPRIO']:
DIRS += ['/third_party/msgpack']

2
config/external/prio/moz.build поставляемый
Просмотреть файл

@ -4,5 +4,5 @@
# 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/.
DIRS += ['../../../third_party/prio']
DIRS += ['/third_party/prio']

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

@ -6,12 +6,12 @@
@import "resource://devtools/client/aboutdebugging-new/src/components/App.css";
@import "resource://devtools/client/aboutdebugging-new/src/components/ConnectPage.css";
@import "resource://devtools/client/aboutdebugging-new/src/components/RuntimeInfo.css";
@import "resource://devtools/client/aboutdebugging-new/src/components/Sidebar.css";
@import "resource://devtools/client/aboutdebugging-new/src/components/SidebarItem.css";
@import "resource://devtools/client/aboutdebugging-new/src/components/debugtarget/DebugTargetItem.css";
@import "resource://devtools/client/aboutdebugging-new/src/components/debugtarget/DebugTargetList.css";
@import "resource://devtools/client/aboutdebugging-new/src/components/debugtarget/ExtensionDetail.css";
@import "resource://devtools/client/aboutdebugging-new/src/components/debugtarget/WorkerDetail.css";
@import "resource://devtools/client/aboutdebugging-new/src/components/sidebar/Sidebar.css";
@import "resource://devtools/client/aboutdebugging-new/src/components/sidebar/SidebarItem.css";
:root {
/* Import css variables from common.css */

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

@ -13,7 +13,7 @@ const { PAGES } = require("../constants");
const ConnectPage = createFactory(require("./ConnectPage"));
const RuntimePage = createFactory(require("./RuntimePage"));
const Sidebar = createFactory(require("./Sidebar"));
const Sidebar = createFactory(require("./sidebar/Sidebar"));
class App extends PureComponent {
static get propTypes() {

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

@ -142,8 +142,11 @@ class ConnectPage extends PureComponent {
{
className: "connect-page__network-form",
onSubmit: (e) => {
addNetworkLocation(this.state.locationInputValue);
this.setState({ locationInputValue: "" });
const locationInputValue = this.state.locationInputValue;
if (locationInputValue) {
addNetworkLocation(locationInputValue);
this.setState({ locationInputValue: "" });
}
e.preventDefault();
}
},
@ -155,9 +158,7 @@ class ConnectPage extends PureComponent {
value: this.state.locationInputValue,
onChange: (e) => {
const locationInputValue = e.target.value;
if (locationInputValue) {
this.setState({ locationInputValue });
}
this.setState({ locationInputValue });
}
}),
dom.button({

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

@ -4,6 +4,7 @@
DIRS += [
'debugtarget',
'sidebar',
]
DevToolsModules(
@ -14,8 +15,4 @@ DevToolsModules(
'RuntimeInfo.css',
'RuntimeInfo.js',
'RuntimePage.js',
'Sidebar.css',
'Sidebar.js',
'SidebarItem.css',
'SidebarItem.js',
)

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

@ -8,7 +8,7 @@ const { createFactory, PureComponent } = require("devtools/client/shared/vendor/
const dom = require("devtools/client/shared/vendor/react-dom-factories");
const PropTypes = require("devtools/client/shared/vendor/react-prop-types");
const { PAGES } = require("../constants");
const { PAGES } = require("../../constants");
const SidebarItem = createFactory(require("./SidebarItem"));
const FIREFOX_ICON = "chrome://devtools/skin/images/aboutdebugging-firefox-logo.svg";

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

@ -8,7 +8,7 @@ const { PureComponent } = require("devtools/client/shared/vendor/react");
const dom = require("devtools/client/shared/vendor/react-dom-factories");
const PropTypes = require("devtools/client/shared/vendor/react-prop-types");
const Actions = require("../actions/index");
const Actions = require("../../actions/index");
/**
* This component displays an item of the Sidebar component.

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

@ -0,0 +1,10 @@
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
DevToolsModules(
'Sidebar.css',
'Sidebar.js',
'SidebarItem.css',
'SidebarItem.js',
)

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

@ -33,6 +33,7 @@ loader.lazyRequireGetter(this, "CommandUtils", "devtools/client/shared/developer
loader.lazyRequireGetter(this, "CommandState", "devtools/shared/gcli/command-state", true);
loader.lazyRequireGetter(this, "ResponsiveUIManager", "devtools/client/responsive.html/manager", true);
loader.lazyImporter(this, "ScratchpadManager", "resource://devtools/client/scratchpad/scratchpad-manager.jsm");
loader.lazyRequireGetter(this, "getScreenshotFront", "resource://devtools/shared/fronts/screenshot", true);
const {MultiLocalizationHelper} = require("devtools/shared/l10n");
const L10N = new MultiLocalizationHelper(
@ -106,14 +107,6 @@ Tools.webConsole = {
accesskey: l10n("webConsoleCmd.accesskey"),
ordinal: 2,
url: "chrome://devtools/content/webconsole/index.html",
get browserConsoleUsesHTML() {
return Services.prefs.getBoolPref("devtools.browserconsole.html");
},
get browserConsoleURL() {
return this.browserConsoleUsesHTML ?
"chrome://devtools/content/webconsole/index.html" :
"chrome://devtools/content/webconsole/browserconsole.xul";
},
icon: "chrome://devtools/skin/images/tool-webconsole.svg",
label: l10n("ToolboxTabWebconsole.label"),
menuLabel: l10n("MenuWebconsole.label"),
@ -594,16 +587,17 @@ exports.ToolboxButtons = [
},
{ id: "command-button-screenshot",
description: l10n("toolbox.buttons.screenshot"),
isTargetSupported: target => target.isLocalTab,
onClick(event, toolbox) {
isTargetSupported: target => !target.chrome && target.hasActor("screenshot"),
async onClick(event, toolbox) {
// Special case for screenshot button to check for clipboard preference
const clipboardEnabled = Services.prefs
.getBoolPref("devtools.screenshot.clipboard.enabled");
let args = "--fullpage --file";
const args = { fullpage: true, file: true };
if (clipboardEnabled) {
args += " --clipboard";
args.clipboard = true;
}
CommandUtils.executeOnTarget(toolbox.target, "screenshot " + args);
const screenshotFront = getScreenshotFront(toolbox.target);
await screenshotFront.captureAndSave(toolbox.win, args);
}
},
{ id: "command-button-rulers",

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

@ -10,12 +10,12 @@ const PropTypes = require("devtools/client/shared/vendor/react-prop-types");
const { KeyCodes } = require("devtools/client/shared/keycodes");
// Milliseconds between auto-increment interval iterations.
const AUTOINCREMENT_DELAY = 300;
const AUTOINCREMENT_DELAY = 1000;
class FontPropertyValue extends PureComponent {
static get propTypes() {
return {
allowAutoIncrement: PropTypes.bool,
autoIncrement: PropTypes.bool,
className: PropTypes.string,
defaultValue: PropTypes.oneOfType([ PropTypes.string, PropTypes.number ]),
label: PropTypes.string.isRequired,
@ -23,7 +23,7 @@ class FontPropertyValue extends PureComponent {
max: PropTypes.number.isRequired,
name: PropTypes.string.isRequired,
onChange: PropTypes.func.isRequired,
step: PropTypes.oneOfType([ PropTypes.string, PropTypes.number ]),
step: PropTypes.number,
unit: PropTypes.oneOfType([ PropTypes.string, null ]),
unitOptions: PropTypes.array,
value: PropTypes.number,
@ -32,7 +32,9 @@ class FontPropertyValue extends PureComponent {
static get defaultProps() {
return {
autoIncrement: false,
className: "",
step: 1,
unitOptions: []
};
}
@ -59,6 +61,7 @@ class FontPropertyValue extends PureComponent {
this.onKeyDown = this.onKeyDown.bind(this);
this.onKeyUp = this.onKeyUp.bind(this);
this.onMouseDown = this.onMouseDown.bind(this);
this.onMouseMove = this.onMouseMove.bind(this);
this.onMouseUp = this.onMouseUp.bind(this);
this.onUnitChange = this.onUnitChange.bind(this);
this.stopAutoIncrement = this.stopAutoIncrement.bind(this);
@ -100,7 +103,7 @@ class FontPropertyValue extends PureComponent {
/**
* Check if the given value is valid according to the constraints of this component.
* Ensure it is a number and that it does not go outside the min/max limits, unless
* allowed by the `allowAutoIncrement` props flag.
* allowed by the `autoIncrement` props flag.
*
* @param {Number} value
* Numeric value
@ -108,7 +111,7 @@ class FontPropertyValue extends PureComponent {
* Whether the value conforms to the components contraints.
*/
isValueValid(value) {
const { allowAutoIncrement, min, max } = this.props;
const { autoIncrement, min, max } = this.props;
if (typeof value !== "number" || isNaN(value)) {
return false;
@ -119,7 +122,7 @@ class FontPropertyValue extends PureComponent {
}
// Ensure it does not exceed maximum value, unless auto-incrementing is permitted.
if (max !== undefined && value > this.props.max && !allowAutoIncrement) {
if (max !== undefined && value > this.props.max && !autoIncrement) {
return false;
}
@ -243,29 +246,33 @@ class FontPropertyValue extends PureComponent {
});
}
/**
* Handler for "keydown" events from the sider and input fields.
* Toggles on the "interactive" state. @See toggleInteractiveState();
* Begins auto-incrementing if the value is already at the upper bound.
*
* @param {Event} e
* MouseDown event.
*/
onMouseDown(e) {
// Begin auto-incrementing if the value is already at the upper bound.
if (this.isAtUpperBound(this.props.value) && e.target.type === "range") {
this.startAutoIncrement();
}
onMouseDown() {
this.toggleInteractiveState(true);
}
onMouseUp(e) {
/**
* Handler for "mousemove" event from range input. If the user is actively interacting
* by dragging the slider thumb, start or stop the auto-incrementing behavior depending
* on whether the input value is at the upper bound or not.
*
* @param {MouseEvent} e
*/
onMouseMove(e) {
if (this.state.interactive && e.buttons) {
this.isAtUpperBound(this.props.value)
? this.startAutoIncrement()
: this.stopAutoIncrement();
}
}
onMouseUp() {
this.stopAutoIncrement();
this.toggleInteractiveState(false);
}
startAutoIncrement() {
// Do not set auto-increment interval if not allowed to or if one is already set.
if (!this.props.allowAutoIncrement || this.interval) {
if (!this.props.autoIncrement || this.interval) {
return;
}
@ -360,7 +367,7 @@ class FontPropertyValue extends PureComponent {
onBlur: this.onBlur,
onChange: this.onChange,
onFocus: this.onFocus,
step: this.props.step || 1,
step: this.props.step,
// While interacting with the range and number inputs, prevent updating value from
// outside props which is debounced and causes jitter on successive renders.
value: this.state.interactive
@ -374,6 +381,7 @@ class FontPropertyValue extends PureComponent {
onKeyDown: this.onKeyDown,
onKeyUp: this.onKeyUp,
onMouseDown: this.onMouseDown,
onMouseMove: this.onMouseMove,
onMouseUp: this.onMouseUp,
className: "font-value-slider",
name: this.props.name,
@ -386,7 +394,7 @@ class FontPropertyValue extends PureComponent {
{
...defaults,
// Remove upper limit from number input if it is allowed to auto-increment.
max: this.props.allowAutoIncrement ? null : this.props.max,
max: this.props.autoIncrement ? null : this.props.max,
name: this.props.name,
className: "font-value-input",
type: "number",

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

@ -58,7 +58,7 @@ class FontSize extends PureComponent {
: max;
return FontPropertyValue({
allowAutoIncrement: true,
autoIncrement: true,
label: getStr("fontinspector.fontSizeLabel"),
min: 0,
max: this.historicMax[unit],

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

@ -61,7 +61,7 @@ class LineHeight extends PureComponent {
: max;
return FontPropertyValue({
allowAutoIncrement: true,
autoIncrement: true,
label: getStr("fontinspector.lineHeightLabel"),
min: 0,
max: this.historicMax[unit],

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

@ -33,9 +33,10 @@ loader.lazyRequireGetter(this, "nodeConstants", "devtools/shared/dom-node-consta
loader.lazyRequireGetter(this, "Menu", "devtools/client/framework/menu");
loader.lazyRequireGetter(this, "MenuItem", "devtools/client/framework/menu-item");
loader.lazyRequireGetter(this, "ExtensionSidebar", "devtools/client/inspector/extensions/extension-sidebar");
loader.lazyRequireGetter(this, "CommandUtils", "devtools/client/shared/developer-toolbar", true);
loader.lazyRequireGetter(this, "clipboardHelper", "devtools/shared/platform/clipboard");
loader.lazyRequireGetter(this, "openContentLink", "devtools/client/shared/link", true);
loader.lazyRequireGetter(this, "getScreenshotFront", "devtools/shared/fronts/screenshot", true);
loader.lazyRequireGetter(this, "saveScreenshot", "devtools/shared/screenshot/save");
loader.lazyImporter(this, "DeferredTask", "resource://gre/modules/DeferredTask.jsm");
@ -2318,21 +2319,22 @@ Inspector.prototype = {
* Initiate gcli screenshot command on selected node.
*/
async screenshotNode() {
const command = Services.prefs.getBoolPref("devtools.screenshot.clipboard.enabled") ?
"screenshot --file --clipboard --selector" :
"screenshot --file --selector";
// Bug 1332936 - it's possible to call `screenshotNode` while the BoxModel highlighter
// is still visible, therefore showing it in the picture.
// To avoid that, we have to hide it before taking the screenshot. The `hideBoxModel`
// will do that, calling `hide` for the highlighter only if previously shown.
await this.highlighter.hideBoxModel();
// Bug 1180314 - CssSelector might contain white space so need to make sure it is
// passed to screenshot as a single parameter. More work *might* be needed if
// CssSelector could contain escaped single- or double-quotes, backslashes, etc.
CommandUtils.executeOnTarget(this._target,
`${command} '${this.selectionCssSelector}'`);
const clipboardEnabled = Services.prefs
.getBoolPref("devtools.screenshot.clipboard.enabled");
const args = {
file: true,
selector: this.selectionCssSelector,
clipboard: clipboardEnabled
};
const screenshotFront = getScreenshotFront(this.target);
const screenshot = await screenshotFront.capture(args);
await saveScreenshot(this.panelWin, args, screenshot);
},
/**

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

@ -149,7 +149,6 @@ devtools.jar:
skin/images/editor-error.png (themes/images/editor-error.png)
skin/images/breakpoint.svg (themes/images/breakpoint.svg)
skin/webconsole.css (themes/webconsole.css)
skin/images/webconsole.svg (themes/images/webconsole.svg)
skin/images/webconsole/alert.svg (themes/images/webconsole/alert.svg)
skin/images/webconsole/info.svg (themes/images/webconsole/info.svg)
skin/images/webconsole/input.svg (themes/images/webconsole/input.svg)

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

@ -64,7 +64,7 @@ pref("devtools.new-animationinspector.enabled", true);
// Enable the Font Editor
pref("devtools.inspector.fonteditor.enabled", true);
// Enable the font highlight-on-hover feature
pref("devtools.inspector.fonthighlighter.enabled", false);
pref("devtools.inspector.fonthighlighter.enabled", true);
// Flexbox preferences
// Enable the Flexbox highlighter in Nightly

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

@ -4465,7 +4465,17 @@ class Tree extends Component {
if (focused || !nativeEvent || !this.treeRef) {
return;
}
this._focus(traversal[0].item);
const { explicitOriginalTarget } = nativeEvent;
// Only set default focus to the first tree node if the focus came
// from outside the tree (e.g. by tabbing to the tree from other
// external elements).
if (
explicitOriginalTarget !== this.treeRef &&
!this.treeRef.contains(explicitOriginalTarget)
) {
this._focus(traversal[0].item);
}
},
onBlur: this._onBlur,
"aria-label": this.props.label,

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

@ -31,16 +31,17 @@
.dbg-wasm-item .icon {
display: block;
background-image: url(chrome://devtools/skin/images/webconsole.svg);
background-repeat: no-repeat;
background-size: 72px 60px;
/* show warning icon */
background-position: -24px -24px;
width: 10px;
height: 10px;
position: absolute;
margin-inline-start: -15px;
margin-top: 3px;
/* show warning icon */
background-image: var(--theme-console-alert-image);
background-repeat: no-repeat;
background-size: contain;
-moz-context-properties: fill;
fill: #ec8633;
}
.dbg-breakpoint-line {

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

@ -1,101 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!-- This Source Code Form is subject to the terms of the Mozilla Public
- License, v. 2.0. If a copy of the MPL was not distributed with this
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="72" height="60" viewBox="0 0 72 60">
<defs>
<rect id="glyphShape-colorSwatch" width="8" height="8" ry="2" rx="2"/>
<rect id="glyphShape-colorSwatch-border" width="10" height="10" ry="2" rx="2"/>
<polygon id="glyphShape-errorX" points="9.9,8.5 8.5,9.9 6,7.4 3.6,9.8 2.2,8.4 4.6,6 2.2,3.6 3.6,2.2 6,4.6 8.4,2.2 9.8,3.6 7.4,6"/>
<path id="glyphShape-warningTriangle" d="M9.9,8.6l-3.1-6C6.6,2.2,6.3,2,6,2C5.7,2,5.4,2.2,5.2,2.5l-3.1,6C2,8.9,2,9.3,2.1,9.6C2.3,9.8,2.6,10,2.9,10 h6.1c0.4,0,0.6-0.2,0.8-0.4C10,9.3,10,8.9,9.9,8.6z"/>
<path id="glyphShape-exclamationPoint" d="M6,7.7c-0.6,0-1,0.4-1,0.8C5,9,5.4,9.3,6,9.3c0.6,0,1-0.4,1-0.8 C7,8.1,6.6,7.7,6,7.7z M6,7c0.6,0,1-0.4,1-1V5c0-0.6-0.4-1-1-1S5,4.4,5,5v1C5,6.6,5.4,7,6,7z"/>
<circle id="glyphShape-infoCircle" cx="6" cy="6" r="4"/>
<path id="glyphShape-infoGlyph" d="M6,6C5.4,6,5,6.4,5,7v1c0,0.6,0.4,1,1,1s1-0.4,1-1V7C7,6.4,6.6,6,6,6z M6,5c0.6,0,1-0.4,1-1S6.6,3,6,3S5,3.4,5,4S5.4,5,6,5z"/>
<style>
.icon-colorSwatch-border {
fill: #fff;
fill-opacity: .7;
}
.icon-colorSwatch-network {
fill: #000;
}
.icon-colorSwatch-css {
fill: #00b6f0;
}
.icon-colorSwatch-js {
fill: #fb9500;
}
.icon-colorSwatch-logging {
fill: #808080;
}
.icon-colorSwatch-security {
fill: #ec1e0d;
}
.icon-glyphOverlay {
fill: #fff;
}
#icon-indicator-input {
fill: #8fa1b2;
}
#icon-indicator-output {
fill: #667380;
}
#light-icons:target #icon-indicator-input {
fill: #45494d;
}
#light-icons:target #icon-indicator-output {
fill: #8a9199;
}
</style>
</defs>
<g id="icon-colorSwatch-network">
<use xlink:href="#glyphShape-colorSwatch-border" class="icon-colorSwatch-border" x="1" y="1"/>
<use xlink:href="#glyphShape-colorSwatch" class="icon-colorSwatch-network" x="2" y="2"/>
</g>
<g id="icon-colorSwatch-css" transform="translate(0 12)">
<use xlink:href="#glyphShape-colorSwatch-border" class="icon-colorSwatch-border" x="1" y="1"/>
<use xlink:href="#glyphShape-colorSwatch" class="icon-colorSwatch-css" x="2" y="2"/>
</g>
<g id="icon-colorSwatch-js" transform="translate(0 24)">
<use xlink:href="#glyphShape-colorSwatch-border" class="icon-colorSwatch-border" x="1" y="1"/>
<use xlink:href="#glyphShape-colorSwatch" class="icon-colorSwatch-js" x="2" y="2"/>
</g>
<g id="icon-colorSwatch-logging" transform="translate(0 36)">
<use xlink:href="#glyphShape-colorSwatch-border" class="icon-colorSwatch-border" x="1" y="1"/>
<use xlink:href="#glyphShape-colorSwatch" class="icon-colorSwatch-logging" x="2" y="2"/>
</g>
<g id="icon-colorSwatch-security" transform="translate(0 48)">
<use xlink:href="#glyphShape-colorSwatch-border" class="icon-colorSwatch-border" x="1" y="1"/>
<use xlink:href="#glyphShape-colorSwatch" class="icon-colorSwatch-security" x="2" y="2"/>
</g>
<use xlink:href="#glyphShape-errorX" id="icon-errorX-network" class="icon-colorSwatch-network" transform="translate(12)"/>
<use xlink:href="#glyphShape-errorX" id="icon-errorX-css" class="icon-colorSwatch-css" transform="translate(12 12)"/>
<use xlink:href="#glyphShape-errorX" id="icon-errorX-js" class="icon-colorSwatch-js" transform="translate(12 24)"/>
<use xlink:href="#glyphShape-errorX" id="icon-errorX-logging" class="icon-colorSwatch-logging" transform="translate(12 36)"/>
<use xlink:href="#glyphShape-errorX" id="icon-errorX-security" class="icon-colorSwatch-security" transform="translate(12 48)"/>
<g id="icon-warningTriangle-css" transform="translate(24 12)">
<use xlink:href="#glyphShape-warningTriangle" class="icon-colorSwatch-css"/>
<use xlink:href="#glyphShape-exclamationPoint" class="icon-glyphOverlay"/>
</g>
<g id="icon-warningTriangle-js" transform="translate(24 24)">
<use xlink:href="#glyphShape-warningTriangle" class="icon-colorSwatch-js"/>
<use xlink:href="#glyphShape-exclamationPoint" class="icon-glyphOverlay"/>
</g>
<g id="icon-warningTriangle-logging" transform="translate(24 36)">
<use xlink:href="#glyphShape-warningTriangle" class="icon-colorSwatch-logging"/>
<use xlink:href="#glyphShape-exclamationPoint" class="icon-glyphOverlay"/>
</g>
<g id="icon-warningTriangle-security" transform="translate(24 48)">
<use xlink:href="#glyphShape-warningTriangle" class="icon-colorSwatch-security"/>
<use xlink:href="#glyphShape-exclamationPoint" class="icon-glyphOverlay"/>
</g>
<g id="icon-infoCircle-logging" transform="translate(36 36)">
<use xlink:href="#glyphShape-infoCircle" class="icon-colorSwatch-logging"/>
<use xlink:href="#glyphShape-infoGlyph" class="icon-glyphOverlay"/>
</g>
<g id="light-icons">
<path id="icon-indicator-input" d="M6.5,1.2L5.4,2.3L9,6L5.3,9.7l1.1,1.1L11,6L6.5,1.2z M1.5,1.2 L0.4,2.3L4,6L0.3,9.7l1.1,1.1L6,6L1.5,1.2z" transform="translate(48 36)"/>
<polygon id="icon-indicator-output" points="10,5 4.3,5 6.8,2.4 5.5,1.2 1,6 5.5,10.8 6.9,9.6 4.3,7 10,7" transform="translate(60 36)"/>
</g>
</svg>

До

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

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

@ -3,5 +3,5 @@
- 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/. -->
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 12 12" fill="context-fill #0b0b0b">
<path d="M1.12 9.4L5 1.6c.41-.81 1.58-.81 2 0l3.88 7.78a1.11 1.11 0 0 1-1 1.61H2.11a1.11 1.11 0 0 1-1-1.6zM6 8.4c-.55 0-1 .4-1 .9s.45.9 1 .9 1-.4 1-.9-.45-.9-1-.9zm0-4.9c-.55 0-1 .36-1 .8v2.4c0 .44.45.8 1 .8s1-.36 1-.8V4.3c0-.44-.45-.8-1-.8z"/>
<path d="M1.12 9.4L5 1.6c.41-.81 1.58-.81 2 0l3.88 7.78a1.11 1.11 0 0 1-1 1.61H2.11a1.11 1.11 0 0 1-1-1.6zM6 7.8a1 1 0 1 0 0 2 1 1 0 0 0 0-2zm0-4.4a1 1 0 0 0-1 1V6a1 1 0 1 0 2 0V4.4a1 1 0 0 0-1-1z" />
</svg>

До

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

После

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

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

@ -3,5 +3,5 @@
- 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/. -->
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 12 12" fill="context-fill #0b0b0b">
<path d="M6 1a5 5 0 1 1 0 10A5 5 0 0 1 6 1zm0 1.5a1 1 0 1 1 0 2 1 1 0 0 1 0-2zm0 3c.55 0 1 .36 1 .8v2.4c0 .44-.45.8-1 .8s-1-.36-1-.8V6.3c0-.44.45-.8 1-.8z" fill-rule="evenodd"/>
<path d="M6 1a5 5 0 1 1 0 10A5 5 0 0 1 6 1zm0 1.6a1 1 0 1 0 0 2 1 1 0 0 0 0-2zm0 2.8a1 1 0 0 0-1 1V8a1 1 0 1 0 2 0V6.4a1 1 0 0 0-1-1z"/>
</svg>

До

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

После

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

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

@ -75,31 +75,24 @@
color: var(--theme-highlight-red);
}
.theme-dark .opt-icon::before {
background-image: url(chrome://devtools/skin/images/webconsole.svg);
}
.theme-light .opt-icon::before {
background-image: url(chrome://devtools/skin/images/webconsole.svg#light-icons);
}
.opt-icon::before {
display: inline-block;
content: "";
background-repeat: no-repeat;
background-size: 72px 60px;
/* show grey "i" bubble by default */
background-position: -36px -36px;
width: 10px;
height: 10px;
display: inline-block;
vertical-align: -2px;
width: 12px;
height: 12px;
max-height: 12px;
}
.opt-icon::before {
margin: 1px 6px 0 0;
margin-inline-end: 5px;
background-image: var(--theme-console-info-image);
background-repeat: no-repeat;
background-size: contain;
-moz-context-properties: fill;
fill: #808080;
}
.opt-icon.warning::before {
background-position: -24px -24px;
background-image: var(--theme-console-alert-image);
fill: #ec8633;
}
/* Frame Component */

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

@ -557,20 +557,16 @@ html, body, #app, #memory-tool {
.error::before {
content: "";
display: inline-block;
vertical-align: -2px;
width: 12px;
height: 12px;
max-height: 12px;
background-image: url(chrome://devtools/skin/images/webconsole.svg);
background-size: 72px 60px;
background-position: -24px -24px;
background-repeat: no-repeat;
margin: 0px;
margin-top: 2px;
margin-inline-end: 5px;
}
.theme-light .error::before {
background-image: url(chrome://devtools/skin/images/webconsole.svg#light-icons);
background-image: var(--theme-console-alert-image);
background-repeat: no-repeat;
background-size: contain;
-moz-context-properties: fill;
fill: #ec8633;
}
/**

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

@ -761,19 +761,17 @@ menuitem.marker-color-graphs-grey .menu-iconic-left::after,
*/
menuitem.experimental-option::before {
content: "";
background-image: url(chrome://devtools/skin/images/webconsole.svg);
background-repeat: no-repeat;
background-size: 72px 60px;
display: inline-block;
width: 12px;
height: 12px;
display: inline-block;
background-position: -24px -24px;
margin: 2px 5px 0 0;
max-height: 12px;
}
.theme-light menuitem.experimental-option::before {
background-image: url(chrome://devtools/skin/images/webconsole.svg#light-icons);
margin-top: 2px;
margin-inline-end: 5px;
background-image: var(--theme-console-alert-image);
background-repeat: no-repeat;
background-size: contain;
-moz-context-properties: fill;
fill: currentColor;
}
#performance-options-menupopup:not(.experimental-enabled) .experimental-option,

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

@ -285,15 +285,18 @@ a {
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
cursor: default;
}
.message.network .status {
flex: none;
margin-inline-start: 6px;
cursor: default;
}
.message.network.mixed-content .url {
color: var(--theme-highlight-red);
cursor: default;
}
.message .learn-more-link {

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

@ -6,125 +6,39 @@ It can also display network logs, and you can evaluate expressions using the con
input, a.k.a. JsTerm. You can read more about it on [MDN](https://developer.mozilla.org/en-US/docs/Tools/Web_Console)
to learn all the features and how to use the tool.
## Old / New frontend
The current console used in the toolbox is called the new frontend, and the code lives at
the root of the `devtools/client/webconsole/` folder.
The old console code is located in the `devtools/client/webconsole/old` folder.
Both frontends use the same code for the console input, also called JsTerm (see `jsterm.js`).
The old frontend is still used for the Browser Console, but is planned to be removed in the
near future (see Bug 1381834).
## Run WebConsole in DevTools panel
## Run WebConsole
If you want to build the WebConsole inside of the DevTools toolbox (Firefox Devtools Panels),
follow the [simple Firefox build](http://docs.firefox-dev.tools/getting-started/build.html)
document in MDN. Start your compiled firefox and open the Firefox developer tool, you can
documentation. Start your compiled firefox and open the Firefox developer tool, you can
then see the WebConsole tab.
## Run WebConsole in a browser tab (experimental)
### Prerequisite
If you would like to run the WebConsole in the browser tab, you need following packages:
* [node](https://nodejs.org/) >= 7.10.0 JavaScript runtime.
* [yarn](https://yarnpkg.com/docs/install) >= 1.0.0 the package dependency management tool.
* [Firefox](https://www.mozilla.org/firefox/new/) any version or build from the source code.
### Run WebConsole
Navigate to the `mozilla-central/devtools/client/webconsole` folder with your terminal.
Note that this folder is available after `mozilla-central` was cloned in order to get a
local copy of the repository. Then run the following commands:
```bash
# Install packages
yarn install
# Create a dev server instance for hosting webconsole on browser
yarn start
```
Open `localhost:8000` to see what we call the [launchpad](https://github.com/devtools-html/devtools-core/tree/master/packages/devtools-launchpad).
The UI that let you start a new Firefox window which will be the target (or debuggee).
Launchpad will communicate with Firefox (the remote debugging server) and list all opened tabs from Firefox.
You can then navigate to a website you want in the target window, and you should see it appears
in the `localhost:8000` page. Clicking on it will start the WebConsole in the browser tab.
### How it works
The WebConsole uses [webpack](https://webpack.js.org/) and several packages from [devtools-core](https://github.com/devtools-html/devtools-core)
to run as a normal web page. The WebConsole uses [Mozilla remote debugging protocol](http://searchfox.org/mozilla-central/source/devtools/docs/backend/protocol.md)
to fetch messages and execute commands against Firefox.
Open `localhost:8000` in any browser to see the
interface. Devtools Launchpad will communicate with Firefox (the remote debugging server)
and list all opened tabs from Firefox. Click one of the browser tab entry, now you can see
the WebConsole runs in a browser tab.
### DevTools shared modules
When working on console running via launchpad, you may need to modify code on external modules.
Besides the third party modules, here are modules required for the WebConsole
(hosted under the `devtools-core` Github repo, which contains modules shared across Devtools).
* [devtools-config](https://github.com/devtools-html/devtools-core/blob/master/packages/devtools-config/#readme) config used in dev server
* [devtools-launchpad](https://github.com/devtools-html/devtools-core/blob/master/packages/devtools-launchpad/#readme) provide the dev server, landing page and the bootstrap functions to run devtools in the browser tab.
* [devtools-modules](https://github.com/devtools-html/devtools-core/blob/master/packages/devtools-modules/#readme) Devtools shared and shim modules.
* [devtools-source-editor](https://github.com/devtools-html/devtools-core/blob/master/packages/devtools-source-editor/#readme) Source Editor component.
* [devtools-reps](https://github.com/devtools-html/debugger.html/blob/master/packages/devtools-reps/#readme) remote object formatter for variables representation.
Changes to those modules need to be done on Github, using the Pull Request workflow.
Then, a new version of the modified package need to be released on npm so the version number
can be updated in WebConsole's `package.json`. Some modules have a release process,
look for `RELEASE.md` file in the module folder, or ask a maintainer if you are
unsure about the release process.
## Code Structure
Top level files are used to launch the WebConsole inside of the DevTools toolbox or run in
the browser tab (experimental). The same code base is used to run in both environments.
### Run inside of the DevTools toolbox
Files used to run the WebConsole inside of the DevTools toolbox.
Top level files are used to launch the WebConsole inside of the DevTools toolbox.
The main files used to run the WebConsole are:
* `main.js` called by devtools toolbox to launch the WebConsole panel.
* `index.html` panel UI and launch scripts.
### Run in the browser tab (experimental)
Files used to run the WebConsole in the browser tab
* `bin/` files to launch test server.
* `configs/` dev configs.
* `local-dev/index.js` the entry point, equivalent to `index.html`.
* `webpack.config.js` the webpack config file, including plenty of module aliases map to shims and polyfills.
* `package.json` declare every required packages and available commands.
To run in the browser tab, the WebConsole needs to get some dependencies from npm module.
Check `package.json` to see all dependencies. Check `webpack.config.js` to find the module alias,
and check [devtools-core](https://github.com/devtools-html/devtools-core) packages to dive
into actual modules used by the WebConsole and other Devtools.
### UI
The WebConsole UI is built using [React](http://docs.firefox-dev.tools/frontend/react.html)
components (in `components/`).
The React application is rendered from `webconsole-output-wrapper.js`.
It contains 3 top components:
It contains 4 top components:
* **ConsoleOutput** (in `ConsoleOutput.js`) is the component where messages are rendered.
* **FilterBar** (in `FilterBar.js`) is the component for the filter bars (filter input and toggle buttons).
* **SideBar** (in `SideBar.js`) is the component that render the sidebar where objects can be placed in.
* **JsTerm** (in `JsTerm.js`) is the component that render the console input.
We prefer stateless component (defined by function) instead of stateful component
(defined by class) unless the component has to maintain its internal state.
### State
Besides the UI, the WebConsole manages the app state via [Redux](When working on console running via launchpad).
Besides the UI, the WebConsole manages the app state via [Redux].
The following locations define the app state:
* `src/constants.js` constants used across the tool including action and event names.
@ -137,8 +51,12 @@ The redux state is a plain javascript object with the following properties:
{
// State of the filter input and toggle buttons
filters,
// State of the input history
history,
// Console messages data and state (hidden, expanded, groups, …)
messages,
// State of notifications displayed on the output (e.g. self-XSS warning message)
notifications,
// Preferences (persist message, message limit, …)
prefs,
// Interface state (filter bar visible, sidebar visible, …)

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

@ -1,29 +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/. */
/* eslint-env node */
"use strict";
const fs = require("fs");
const path = require("path");
function getConfig() {
if (process.env.TARGET === "firefox-panel") {
return require("../configs/firefox-panel.json");
}
const developmentConfig = require("../configs/development.json");
let localConfig = {};
if (fs.existsSync(path.resolve(__dirname, "../configs/local.json"))) {
localConfig = require("../configs/local.json");
}
return Object.assign({}, developmentConfig, localConfig);
}
module.exports = {
getConfig,
};

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

@ -1,19 +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/. */
/* eslint-env node */
"use strict";
const toolbox = require("devtools-launchpad/index");
const feature = require("devtools-config");
const { getConfig } = require("./configure");
const envConfig = getConfig();
feature.setConfig(envConfig);
const webpackConfig = require("../webpack.config");
toolbox.startDevServer(envConfig, webpackConfig, __dirname);

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

@ -18,7 +18,7 @@ loader.lazyRequireGetter(this, "gDevTools", "devtools/client/framework/devtools"
loader.lazyRequireGetter(this, "KeyCodes", "devtools/client/shared/keycodes", true);
loader.lazyRequireGetter(this, "Editor", "devtools/client/sourceeditor/editor");
loader.lazyRequireGetter(this, "Telemetry", "devtools/client/shared/telemetry");
loader.lazyRequireGetter(this, "processScreenshot", "devtools/shared/webconsole/screenshot-helper");
loader.lazyRequireGetter(this, "saveScreenshot", "devtools/shared/screenshot/save");
const l10n = require("devtools/client/webconsole/webconsole-l10n");
@ -473,7 +473,7 @@ class JSTerm extends Component {
break;
case "screenshotOutput":
const { args, value } = helperResult;
const results = await processScreenshot(this.hud.window, args, value);
const results = await saveScreenshot(this.hud.window, args, value);
this.screenshotNotify(results);
// early return as screenshot notify has dispatched all necessary messages
return null;
@ -1104,7 +1104,15 @@ class JSTerm extends Component {
filterBy = input.substring(input.lastIndexOf(lastNonAlpha) + 1);
}
const newList = this._autocompleteCache.sort().filter(l => l.startsWith(filterBy));
const filterByLc = filterBy.toLocaleLowerCase();
const looseMatching = !filterBy || filterBy[0].toLocaleLowerCase() === filterBy[0];
const newList = this._autocompleteCache.filter(l => {
if (looseMatching) {
return l.toLocaleLowerCase().startsWith(filterByLc);
}
return l.startsWith(filterBy);
});
this._receiveAutocompleteProperties(null, {
matches: newList,
@ -1146,21 +1154,42 @@ class JSTerm extends Component {
this._autocompleteQuery = inputUntilCursor;
}
const matches = message.matches;
const lastPart = message.matchProp;
const {matches, matchProp} = message;
if (!matches.length) {
this.clearCompletion();
this.emit("autocomplete-updated");
return;
}
const items = matches.map(match => ({
preLabel: match.substring(0, matchProp.length),
label: match
}));
if (items.length > 0) {
const suffix = items[0].label.substring(matchProp.length);
this.setAutoCompletionText(suffix);
}
const popup = this.autocompletePopup;
const items = matches.map(match => ({ preLabel: lastPart, label: match }));
popup.setItems(items);
const minimumAutoCompleteLength = 2;
if (items.length >= minimumAutoCompleteLength) {
// We want to show the autocomplete popup if:
// - there are at least 2 matching results
// - OR, if there's 1 result, but whose label does not start like the input (this can
// happen with insensitive search: `num` will match `Number`).
// - OR, if there's 1 result, but we can't show the completionText (because there's
// some text after the cursor), unless the text in the popup is the same as the input.
if (items.length >= minimumAutoCompleteLength
|| (items.length === 1 && items[0].preLabel !== matchProp)
|| (
items.length === 1
&& !this.canDisplayAutoCompletionText()
&& items[0].label !== matchProp
)
) {
let popupAlignElement;
let xOffset;
let yOffset;
@ -1168,12 +1197,12 @@ class JSTerm extends Component {
if (this.editor) {
popupAlignElement = this.node.querySelector(".CodeMirror-cursor");
// We need to show the popup at the ".".
xOffset = -1 * lastPart.length * this._inputCharWidth;
xOffset = -1 * matchProp.length * this._inputCharWidth;
yOffset = 5;
} else if (this.inputNode) {
const offset = inputUntilCursor.length -
(inputUntilCursor.lastIndexOf("\n") + 1) -
lastPart.length;
matchProp.length;
xOffset = (offset * this._inputCharWidth) + this._chevronWidth;
popupAlignElement = this.inputNode;
}
@ -1185,10 +1214,6 @@ class JSTerm extends Component {
popup.hidePopup();
}
if (items.length > 0) {
const suffix = items[0].label.substring(lastPart.length);
this.setAutoCompletionText(suffix);
}
this.emit("autocomplete-updated");
}
@ -1234,22 +1259,21 @@ class JSTerm extends Component {
*/
acceptProposedCompletion() {
let completionText = this.getAutoCompletionText();
// In some cases the completion text might not be displayed (e.g. there is some text
// just after the cursor so we can't display it). In those case, if the popup is
// open and has a selectedItem, we use it for completing the input.
if (
!completionText
&& this.autocompletePopup.isOpen
&& this.autocompletePopup.selectedItem
) {
let numberOfCharsToReplaceCharsBeforeCursor;
// If the autocompletion popup is open, we always get the selected element from there,
// since the autocompletion text might not be enough (e.g. `dOcUmEn` should
// autocomplete to `document`, but the autocompletion text only shows `t`).
if (this.autocompletePopup.isOpen && this.autocompletePopup.selectedItem) {
const {selectedItem} = this.autocompletePopup;
completionText = selectedItem.label.substring(selectedItem.preLabel.length);
completionText = selectedItem.label;
numberOfCharsToReplaceCharsBeforeCursor = selectedItem.preLabel.length;
}
this.clearCompletion();
if (completionText) {
this.insertStringAtCursor(completionText);
this.insertStringAtCursor(completionText, numberOfCharsToReplaceCharsBeforeCursor);
}
}
@ -1270,16 +1294,21 @@ class JSTerm extends Component {
* Insert a string into the console at the cursor location,
* moving the cursor to the end of the string.
*
* @param string str
* @param {string} str
* @param {int} numberOfCharsToReplaceCharsBeforeCursor - defaults to 0
*/
insertStringAtCursor(str) {
insertStringAtCursor(str, numberOfCharsToReplaceCharsBeforeCursor = 0) {
const value = this.getInputValue();
const prefix = this.getInputValueBeforeCursor();
let prefix = this.getInputValueBeforeCursor();
const suffix = value.replace(prefix, "");
if (numberOfCharsToReplaceCharsBeforeCursor) {
prefix =
prefix.substring(0, prefix.length - numberOfCharsToReplaceCharsBeforeCursor);
}
// We need to retrieve the cursor before setting the new value.
const editorCursor = this.editor && this.editor.getCursor();
this.setInputValue(prefix + str + suffix);
if (this.inputNode) {
@ -1289,7 +1318,7 @@ class JSTerm extends Component {
// Set the cursor on the same line it was already at, after the autocompleted text
this.editor.setCursor({
line: editorCursor.line,
ch: editorCursor.ch + str.length
ch: editorCursor.ch + str.length - numberOfCharsToReplaceCharsBeforeCursor
});
}
}

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

@ -106,7 +106,7 @@ function NetworkEventMessage({
const xhr = isXHR
? dom.span({ className: "xhr" }, l10n.getStr("webConsoleXhrIndicator"))
: null;
const requestUrl = dom.a({ className: "url", title: request.url, onClick: toggle },
const requestUrl = dom.span({ className: "url", title: request.url, onClick: toggle },
request.url);
const statusBody = statusInfo
? dom.a({ className: "status", onClick: toggle }, statusInfo)

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

@ -1,99 +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/. */
/* eslint-env browser */
"use strict";
const React = require("react");
const ReactDOM = require("react-dom");
const { EventEmitter } = require("devtools-modules");
const { Services: { appinfo, pref } } = require("devtools-modules");
const { bootstrap } = require("devtools-launchpad");
EventEmitter.decorate(window);
require("../../themes/widgets.css");
require("../../themes/webconsole.css");
require("../../themes/components-frame.css");
require("../../themes/light-theme.css");
require("../../shared/components/reps/reps.css");
require("../../shared/components/tabs/Tabs.css");
require("../../shared/components/tabs/TabBar.css");
require("../../netmonitor/src/assets/styles/httpi.css");
pref("devtools.debugger.remote-timeout", 10000);
pref("devtools.hud.loglimit", 10000);
pref("devtools.webconsole.filter.error", true);
pref("devtools.webconsole.filter.warn", true);
pref("devtools.webconsole.filter.info", true);
pref("devtools.webconsole.filter.log", true);
pref("devtools.webconsole.filter.debug", true);
pref("devtools.webconsole.filter.css", false);
pref("devtools.webconsole.filter.net", false);
pref("devtools.webconsole.filter.netxhr", false);
pref("devtools.webconsole.ui.filterbar", false);
pref("devtools.webconsole.inputHistoryCount", 50);
pref("devtools.webconsole.persistlog", false);
pref("devtools.webconsole.timestampMessages", false);
pref("devtools.webconsole.sidebarToggle", true);
const WebConsoleOutputWrapper = require("../webconsole-output-wrapper");
const WebConsoleFrame = require("../webconsole-frame").WebConsoleFrame;
// Copied from netmonitor/index.js:
window.addEventListener("DOMContentLoaded", () => {
for (const link of document.head.querySelectorAll("link")) {
link.href = link.href.replace(/(resource|chrome)\:\/\//, "/");
}
if (appinfo.OS === "Darwin") {
document.documentElement.setAttribute("platform", "mac");
} else if (appinfo.OS === "Linux") {
document.documentElement.setAttribute("platform", "linux");
} else {
document.documentElement.setAttribute("platform", "win");
}
});
let consoleFrame;
function onConnect(connection) {
// If we are on the main dashboard don't render the component
if (!connection || !connection.tabConnection || !connection.tabConnection.tabTarget) {
return;
}
// Replicate the DOM that the root component lives within
document.querySelector("#mount").innerHTML = `
<div id="app-wrapper" class="theme-body">
<div id="output-container" role="document" aria-live="polite" />
</div>
`;
// Stub out properties that are received from hudservice
const owner = {
iframeWindow: window,
chromeWindow: window,
hudId: "hud_0",
getDebuggerFrames: () => { },
getInspectorSelection: () => { },
target: connection.tabConnection.tabTarget,
_browserConsole: false,
WebConsoleOutputWrapper,
};
consoleFrame = new WebConsoleFrame(owner);
consoleFrame.init().then(function() {
console.log("WebConsoleFrame initialized");
});
}
// This is just a hack until the local dev environment includes jsterm
window.evaluateJS = function(input) {
consoleFrame.webConsoleClient.evaluateJSAsync(`${input}`, function(r) {
consoleFrame.consoleOutput.dispatchMessageAdd(r);
}, {});
};
document.documentElement.classList.add("theme-light");
bootstrap(React, ReactDOM).then(onConnect);

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

@ -1,29 +0,0 @@
{
"name": "webconsole",
"version": "0.0.1",
"engines": {
"node": ">=7.10.0"
},
"scripts": {
"start": "cross-env NODE_ENV=production node bin/dev-server",
"dev": " cross-env NODE_ENV=development node bin/dev-server"
},
"dependencies": {
"babel-plugin-transform-flow-strip-types": "^6.22.0",
"babel-plugin-transform-react-jsx": "^6.24.1",
"babel-plugin-transform-object-rest-spread": "^6.26.0",
"cross-env": "^3.1.3",
"devtools-config": "0.0.12",
"devtools-launchpad": "^0.0.119",
"devtools-modules": "0.0.37",
"file-loader": "^1.1.6",
"netmonitor": "file:../netmonitor",
"raw-loader": "^0.5.1",
"react": "=16.2.0",
"react-dom": "=16.2.0",
"react-prop-types": "=0.4.0",
"react-redux": "=5.0.6",
"redux": "^3.7.2",
"require-hacker": "^2.1.4"
}
}

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

@ -1,88 +0,0 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
"use strict";
// Objects and functions were cherry-picked from devtools/shared/client/main.js, in
// order to make the console launchpad Worker.
// mock, we only need DebuggerClient.requester
const DebuggerClient = function(transport) {};
/**
* A declarative helper for defining methods that send requests to the server.
*
* @param packetSkeleton
* The form of the packet to send. Can specify fields to be filled from
* the parameters by using the |arg| function.
* @param before
* The function to call before sending the packet. Is passed the packet,
* and the return value is used as the new packet. The |this| context is
* the instance of the client object we are defining a method for.
* @param after
* The function to call after the response is received. It is passed the
* response, and the return value is considered the new response that
* will be passed to the callback. The |this| context is the instance of
* the client object we are defining a method for.
* @return Request
* The `Request` object that is a Promise object and resolves once
* we receive the response. (See request method for more details)
*/
DebuggerClient.requester = function(packetSkeleton, config = {}) {
const { before, after } = config;
return function(...args) {
let outgoingPacket = {
to: packetSkeleton.to || this.actor
};
let maxPosition = -1;
for (const k of Object.keys(packetSkeleton)) {
if (packetSkeleton[k] instanceof DebuggerClient.Argument) {
const { position } = packetSkeleton[k];
outgoingPacket[k] = packetSkeleton[k].getArgument(args);
maxPosition = Math.max(position, maxPosition);
} else {
outgoingPacket[k] = packetSkeleton[k];
}
}
if (before) {
outgoingPacket = before.call(this, outgoingPacket);
}
return this.request(outgoingPacket, (response) => {
if (after) {
const { from } = response;
response = after.call(this, response);
if (!response.from) {
response.from = from;
}
}
// The callback is always the last parameter.
const thisCallback = args[maxPosition + 1];
if (thisCallback) {
thisCallback(response);
}
return response;
}, "DebuggerClient.requester request callback");
};
};
function arg(pos) {
return new DebuggerClient.Argument(pos);
}
DebuggerClient.Argument = function(position) {
this.position = position;
};
DebuggerClient.Argument.prototype.getArgument = function(params) {
if (!(this.position in params)) {
throw new Error("Bad index into params: " + this.position);
}
return params[this.position];
};
module.exports = { arg, DebuggerClient };

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

@ -200,6 +200,7 @@ skip-if = verify
[browser_jsterm_autocomplete_return_key.js]
[browser_jsterm_autocomplete_width.js]
[browser_jsterm_autocomplete-properties-with-non-alphanumeric-names.js]
[browser_jsterm_completion_case_sensitivity.js]
[browser_jsterm_completion.js]
[browser_jsterm_content_defined_helpers.js]
[browser_jsterm_copy_command.js]

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

@ -49,9 +49,7 @@ async function performTests() {
EventUtils.synthesizeKey("KEY_ArrowRight");
await onPopupClose;
ok(true, "popup was closed");
let expectedInput = "dump(window.testB)";
is(jsterm.getInputValue(), expectedInput, "input wasn't modified");
checkJsTermCursor(jsterm, expectedInput.length, "cursor was moved to the right");
checkJsTermValueAndCursor(jsterm, "dump(window.testB)|", "input wasn't modified");
await setInitialState(jsterm);
EventUtils.synthesizeKey("KEY_ArrowDown");
@ -69,9 +67,8 @@ async function performTests() {
// At this point the completion suggestion should be accepted.
ok(!popup.isOpen, "popup is not open");
expectedInput = "dump(window.testBugBB)";
is(jsterm.getInputValue(), expectedInput, "completion was successful after VK_TAB");
checkJsTermCursor(jsterm, expectedInput.length - 1, "cursor location is correct");
checkJsTermValueAndCursor(jsterm, "dump(window.testBugBB|)",
"completion was successful after VK_TAB");
ok(!getJsTermCompletionValue(jsterm), "there is no completion text");
info("Test ENTER key when popup is visible with a selected item");
@ -82,33 +79,40 @@ async function performTests() {
await onPopupClose;
ok(!popup.isOpen, "popup is not open");
expectedInput = "dump(window.testBugAA)";
is(jsterm.getInputValue(), expectedInput, "completion was successful after Enter");
checkJsTermCursor(jsterm, expectedInput.length - 1, "cursor location is correct");
checkJsTermValueAndCursor(jsterm, "dump(window.testBugAA|)",
"completion was successful after Enter");
ok(!getJsTermCompletionValue(jsterm), "there is no completion text");
info("Test TAB key when there is no autocomplete suggestion");
info("Test autocomplete inside parens");
jsterm.setInputValue("dump()");
EventUtils.synthesizeKey("KEY_ArrowLeft");
const onAutocompleteUpdated = jsterm.once("autocomplete-updated");
EventUtils.sendString("window.testBugA");
await onAutocompleteUpdated;
ok(popup.isOpen, "popup is open");
ok(!getJsTermCompletionValue(jsterm), "there is no completion text");
info("Matching the completion proposal should close the popup");
onPopupClose = popup.once("popup-closed");
EventUtils.sendString("A");
await onPopupClose;
info("Test TAB key when there is no autocomplete suggestion");
ok(!popup.isOpen, "popup is not open");
ok(!getJsTermCompletionValue(jsterm), "there is no completion text");
EventUtils.synthesizeKey("KEY_Tab");
expectedInput = "dump(window.testBugAA)";
is(jsterm.getInputValue(), "dump(window.testBugA\t)", "Tab inserted a tab char");
checkJsTermCursor(jsterm, expectedInput.length - 1, "cursor location is correct");
checkJsTermValueAndCursor(jsterm, "dump(window.testBugAA\t|)",
"completion was successful after Enter");
}
function setInitialState(jsterm) {
async function setInitialState(jsterm) {
jsterm.focus();
jsterm.setInputValue("dump()");
EventUtils.synthesizeKey("KEY_ArrowLeft");
const onPopUpOpen = jsterm.autocompletePopup.once("popup-opened");
EventUtils.sendString("window.testB");
return onPopUpOpen;
checkJsTermValueAndCursor(jsterm, "dump(window.testB|)");
await onPopUpOpen;
}

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

@ -13,11 +13,11 @@ const TEST_URI = `data:text/html;charset=utf-8,
/* Create prototype-less object so popup does not contain native
* Object prototype properties.
*/
window.x = Object.create(null, Object.getOwnPropertyDescriptors({
window.xx = Object.create(null, Object.getOwnPropertyDescriptors({
["y".repeat(10)]: 1,
["z".repeat(20)]: 2
}));
window.xx = 1;
window.xxx = 1;
</script>
</head>
<body>Test</body>`;
@ -36,14 +36,14 @@ async function performTests() {
const onPopUpOpen = popup.once("popup-opened");
info(`wait for completion suggestions for "x"`);
EventUtils.sendString("x");
info(`wait for completion suggestions for "xx"`);
EventUtils.sendString("xx");
await onPopUpOpen;
ok(popup.isOpen, "popup is open");
const expectedPopupItems = ["x", "xx"];
const expectedPopupItems = ["xx", "xxx"];
is(popup.items.map(i => i.label).join("-"), expectedPopupItems.join("-"),
"popup has expected items");
@ -51,7 +51,7 @@ async function performTests() {
ok(originalWidth > 2 * jsterm._inputCharWidth,
"popup is at least wider than the width of the longest list item");
info(`wait for completion suggestions for "x."`);
info(`wait for completion suggestions for "xx."`);
let onAutocompleteUpdated = jsterm.once("autocomplete-updated");
EventUtils.sendString(".");
await onAutocompleteUpdated;
@ -63,7 +63,7 @@ async function performTests() {
ok(newPopupWidth > 20 * jsterm._inputCharWidth,
"popup is at least wider than the width of the longest list item");
info(`wait for completion suggestions for "x"`);
info(`wait for completion suggestions for "xx"`);
onAutocompleteUpdated = jsterm.once("autocomplete-updated");
EventUtils.synthesizeKey("KEY_Backspace");
await onAutocompleteUpdated;

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

@ -7,7 +7,10 @@
"use strict";
const TEST_URI = "data:text/html;charset=utf8,<p>test code completion";
const TEST_URI = `data:text/html;charset=utf8,<p>test code completion
<script>
foobar = true;
</script>`;
add_task(async function() {
// Run test with legacy JsTerm
@ -23,18 +26,18 @@ async function performTests() {
const {autocompletePopup} = jsterm;
// Test typing 'docu'.
await setInputValueForAutocompletion(jsterm, "docu");
is(jsterm.getInputValue(), "docu", "'docu' completion (input.value)");
checkJsTermCompletionValue(jsterm, " ment", "'docu' completion (completeNode)");
await setInputValueForAutocompletion(jsterm, "foob");
is(jsterm.getInputValue(), "foob", "'foob' completion (input.value)");
checkJsTermCompletionValue(jsterm, " ar", "'foob' completion (completeNode)");
is(autocompletePopup.items.length, 1, "autocomplete popup has 1 item");
is(autocompletePopup.isOpen, false, "autocomplete popup is not open");
// Test typing 'docu' and press tab.
EventUtils.synthesizeKey("KEY_Tab");
is(jsterm.getInputValue(), "document", "'docu' tab completion");
is(jsterm.getInputValue(), "foobar", "'foob' tab completion");
checkJsTermCursor(jsterm, "document".length, "cursor is at the end of 'document'");
is(getJsTermCompletionValue(jsterm).replace(/ /g, ""), "", "'docu' completed");
checkJsTermCursor(jsterm, "foobar".length, "cursor is at the end of 'foobar'");
is(getJsTermCompletionValue(jsterm).replace(/ /g, ""), "", "'foob' completed");
// Test typing 'window.Ob' and press tab. Just 'window.O' is
// ambiguous: could be window.Object, window.Option, etc.

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

@ -0,0 +1,101 @@
/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
/* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/ */
// Tests that code completion works properly in regards to case sensitivity.
"use strict";
const TEST_URI = `data:text/html;charset=utf8,<p>test case-sensitivity completion.
<script>
fooBar = Object.create(null, Object.getOwnPropertyDescriptors({
Foo: 1,
test: 2,
Test: 3,
TEST: 4,
}));
FooBar = true;
</script>`;
add_task(async function() {
// Run test with legacy JsTerm
await pushPref("devtools.webconsole.jsterm.codeMirror", false);
await performTests();
// And then run it with the CodeMirror-powered one.
await pushPref("devtools.webconsole.jsterm.codeMirror", true);
await performTests();
});
async function performTests() {
const {jsterm} = await openNewTabAndConsole(TEST_URI);
const {autocompletePopup} = jsterm;
const checkInput = (expected, assertionInfo) =>
checkJsTermValueAndCursor(jsterm, expected, assertionInfo);
info("Check that lowercased input is case-insensitive");
let onPopUpOpen = autocompletePopup.once("popup-opened");
EventUtils.sendString("foob");
await onPopUpOpen;
is(getAutocompletePopupLabels(autocompletePopup).join(" - "), "fooBar - FooBar",
"popup has expected item, in expected order");
checkJsTermCompletionValue(jsterm, " ar", "completeNode has expected value");
info("Check that filtering the autocomplete cache is also case insensitive");
let onAutoCompleteUpdated = jsterm.once("autocomplete-updated");
// Send "a" to make the input "fooba"
EventUtils.sendString("a");
await onAutoCompleteUpdated;
checkInput("fooba|");
is(getAutocompletePopupLabels(autocompletePopup).join(" - "), "fooBar - FooBar",
"popup cache filtering is also case-insensitive");
checkJsTermCompletionValue(jsterm, " r", "completeNode has expected value");
info("Check that accepting the completion value will change the input casing");
let onPopupClose = autocompletePopup.once("popup-closed");
EventUtils.synthesizeKey("KEY_Tab");
await onPopupClose;
checkInput("fooBar|", "The input was completed with the correct casing");
checkJsTermCompletionValue(jsterm, "", "completeNode is empty");
info("Check that the popup is displayed with only 1 matching item");
onPopUpOpen = autocompletePopup.once("popup-opened");
EventUtils.sendString(".f");
await onPopUpOpen;
// Here we want to match "Foo", and since the completion text will only be "oo", we want
// to display the popup so the user knows that we are matching "Foo" and not "foo".
checkInput("fooBar.f|");
ok(true, "The popup was opened even if there's 1 item matching");
is(getAutocompletePopupLabels(autocompletePopup).join(" - "), "Foo",
"popup has expected item");
checkJsTermCompletionValue(jsterm, " oo", "completeNode has expected value");
onPopupClose = autocompletePopup.once("popup-closed");
EventUtils.synthesizeKey("KEY_Tab");
await onPopupClose;
checkInput("fooBar.Foo|", "The input was completed with the correct casing");
checkJsTermCompletionValue(jsterm, "", "completeNode is empty");
jsterm.setInputValue("");
info("Check that filtering the cache works like on the server");
onPopUpOpen = autocompletePopup.once("popup-opened");
EventUtils.sendString("fooBar.");
await onPopUpOpen;
is(getAutocompletePopupLabels(autocompletePopup).join(" - "),
"test - Foo - TEST - Test", "popup has expected items");
onAutoCompleteUpdated = jsterm.once("autocomplete-updated");
EventUtils.sendString("T");
await onAutoCompleteUpdated;
is(getAutocompletePopupLabels(autocompletePopup).join(" - "), "TEST - Test",
"popup was filtered case-sensitively, as expected");
}
function getAutocompletePopupLabels(autocompletePopup) {
return autocompletePopup.items.map(i => i.label);
}

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

@ -39,7 +39,8 @@ add_task(async function() {
});
info("Expand the last node");
lastNode.click();
const view = lastNode.ownerDocument.defaultView;
EventUtils.synthesizeMouseAtCenter(lastNode, {}, view);
await onOiMutation;
is(scrollTop, outputContainer.scrollTop,

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

@ -24,10 +24,6 @@ const PREF_MESSAGE_TIMESTAMP = "devtools.webconsole.timestampMessages";
const PREF_PERSISTLOG = "devtools.webconsole.persistlog";
const PREF_SIDEBAR_ENABLED = "devtools.webconsole.sidebarToggle";
// XXX: This file is incomplete (see bug 1326937).
// It's used when loading the webconsole with devtools-launchpad, but will ultimately be
// the entry point for the new frontend
/**
* A WebConsoleFrame instance is an interactive console initialized *per target*
* that displays console log data as well as provides an interactive terminal to
@ -236,10 +232,8 @@ WebConsoleFrame.prototype = {
const toolbox = gDevTools.getToolbox(this.owner.target);
// Handle both launchpad and toolbox loading
const Wrapper = this.owner.WebConsoleOutputWrapper || this.window.WebConsoleOutput;
this.consoleOutput =
new Wrapper(this.outputNode, this, toolbox, this.owner, this.document);
this.consoleOutput = new this.window.WebConsoleOutput(
this.outputNode, this, toolbox, this.owner, this.document);
// Toggle the timestamp on preference change
Services.prefs.addObserver(PREF_MESSAGE_TIMESTAMP, this._onToolboxPrefChanged);
this._onToolboxPrefChanged();

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

@ -1,160 +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/. */
/* eslint-env node */
/* eslint max-len: [0] */
"use strict";
const {toolboxConfig} = require("./node_modules/devtools-launchpad/index");
const { NormalModuleReplacementPlugin } = require("webpack");
const {getConfig} = require("./bin/configure");
const path = require("path");
const projectPath = path.join(__dirname, "local-dev");
const webpackConfig = {
entry: {
console: [path.join(projectPath, "index.js")],
},
module: {
rules: [
{
test: /\.(png|svg)$/,
loader: "file-loader?name=[path][name].[ext]",
},
{
test: /\.js$/,
loaders: [
/*
* The version of webpack used in the launchpad seems to have trouble
* with the require("raw!${file}") that we use for the properties
* file in l10.js.
* This loader goes through the whole code and remove the "raw!" prefix
* so the raw-loader declared in devtools-launchpad config can load
* those files.
*/
"rewrite-raw",
// Replace all references to this.browserRequire() by require()
"rewrite-browser-require",
// Replace all references to loader.lazyRequire() by require()
"rewrite-lazy-require",
// Replace all references to loader.lazyGetter() by require()
"rewrite-lazy-getter",
],
}
]
},
resolveLoader: {
modules: [
path.resolve("./node_modules"),
path.resolve("../shared/webpack"),
]
},
output: {
path: path.join(__dirname, "assets/build"),
filename: "[name].js",
publicPath: "/assets/build",
},
externals: [
{
"promise": "var Promise",
}
],
};
webpackConfig.resolve = {
modules: [
// Make sure webpack is always looking for modules in
// `webconsole/node_modules` directory first.
path.resolve(__dirname, "node_modules"), "node_modules"
],
alias: {
"Services": "devtools-modules/src/Services",
"devtools/client/webconsole/utils": path.join(__dirname, "test/fixtures/WebConsoleUtils"),
"devtools/client/shared/vendor/immutable": "immutable",
"devtools/client/shared/vendor/react": "react",
"devtools/client/shared/vendor/react-dom": "react-dom",
"devtools/client/shared/vendor/react-redux": "react-redux",
"devtools/client/shared/vendor/redux": "redux",
"devtools/client/shared/vendor/reselect": "reselect",
"resource://gre/modules/AppConstants.jsm": path.join(__dirname, "../../client/shared/webpack/shims/app-constants-stub"),
"devtools/client/framework/devtools": path.join(__dirname, "../../client/shared/webpack/shims/framework-devtools-shim"),
"devtools/client/framework/menu": "devtools-modules/src/menu",
"devtools/client/sourceeditor/editor": "devtools-source-editor/src/source-editor",
"devtools/client/shared/unicode-url": "./node_modules/devtools-modules/src/unicode-url",
"devtools/client/shared/zoom-keys": "devtools-modules/src/zoom-keys",
"devtools/shared/fronts/timeline": path.join(__dirname, "../../client/shared/webpack/shims/fronts-timeline-shim"),
"devtools/shared/event-emitter": "devtools-modules/src/utils/event-emitter",
"devtools/shared/client/debugger-client": path.join(__dirname, "test/fixtures/DebuggerClient"),
"devtools/shared/platform/clipboard": path.join(__dirname, "../../client/shared/webpack/shims/platform-clipboard-stub"),
"devtools/shared/platform/stack": path.join(__dirname, "../../client/shared/webpack/shims/platform-stack-stub"),
// Locales need to be explicitly mapped to the en-US subfolder
"toolkit/locales": path.join(__dirname, "../../../toolkit/locales/en-US"),
"devtools/client/locales": path.join(__dirname, "../../client/locales/en-US"),
"devtools/shared/locales": path.join(__dirname, "../../shared/locales/en-US"),
"devtools/startup/locales": path.join(__dirname, "../../shared/locales/en-US"),
// Unless a path explicitly needs to be rewritten or shimmed, all devtools paths can
// be mapped to ../../
"devtools": path.join(__dirname, "../../"),
}
};
const mappings = [
[
/utils\/menu/, "devtools-launchpad/src/components/shared/menu"
],
[
/chrome:\/\/devtools\/skin/,
(result) => {
result.request = result.request
.replace("./chrome://devtools/skin", path.join(__dirname, "../themes"));
}
],
[
/chrome:\/\/devtools\/content/,
(result) => {
result.request = result.request
.replace("./chrome://devtools/content", path.join(__dirname, ".."));
}
],
[
/resource:\/\/devtools/,
(result) => {
result.request = result.request
.replace("./resource://devtools/client", path.join(__dirname, ".."));
}
],
];
webpackConfig.plugins = mappings.map(([regex, res]) =>
new NormalModuleReplacementPlugin(regex, res));
const basePath = path.join(__dirname, "../../").replace(/\\/g, "\\\\");
const config = toolboxConfig(webpackConfig, getConfig(), {
// Exclude to transpile all scripts in devtools/ but not for this folder nor netmonitor.
babelExcludes: new RegExp(`^${basePath}(.(?!(webconsole|netmonitor)))*$`),
disablePostCSS: true,
});
// Remove loaders from devtools-launchpad's webpack.config.js
// * For svg-inline loader:
// Webconsole uses file loader to bundle image assets instead of svg-inline-loader
config.module.rules = config.module.rules
.filter((rule) => !["svg-inline-loader"].includes(rule.loader));
module.exports = config;

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

@ -57,6 +57,7 @@ DevToolsModules(
'promises.js',
'reflow.js',
'root.js',
'screenshot.js',
'source.js',
'storage.js',
'string.js',

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

@ -0,0 +1,20 @@
/* 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 protocol = require("devtools/shared/protocol");
const {captureScreenshot} = require("devtools/shared/screenshot/capture");
const {screenshotSpec} = require("devtools/shared/specs/screenshot");
exports.ScreenshotActor = protocol.ActorClassWithSpec(screenshotSpec, {
initialize: function(conn, targetActor) {
protocol.Actor.prototype.initialize.call(this, conn);
this.document = targetActor.window.document;
},
capture: function(args) {
return captureScreenshot(args, this.document);
}
});

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

@ -1175,7 +1175,7 @@ WebConsoleActor.prototype =
this.dbg.removeDebuggee(this.evalWindow);
}
matches = result.matches || [];
matches = result.matches || new Set();
matchProp = result.matchProp;
// We consider '$' as alphanumeric because it is used in the names of some
@ -1183,18 +1183,26 @@ WebConsoleActor.prototype =
// be seen as break in the evaled string.
const lastNonAlphaIsDot = /[.][a-zA-Z0-9$\s]*$/.test(reqText);
if (!lastNonAlphaIsDot) {
matches = matches.concat(this._getWebConsoleCommandsCache().filter(n =>
// filter out `screenshot` command as it is inaccessible without
// the `:` prefix
n !== "screenshot" && n.startsWith(result.matchProp)
));
this._getWebConsoleCommandsCache().forEach(n => {
// filter out `screenshot` command as it is inaccessible without the `:` prefix
if (n !== "screenshot" && n.startsWith(result.matchProp)) {
matches.add(n);
}
});
}
}
// Make sure we return an array with unique items, since `matches` can hold twice
// the same function name if it was defined in the content page and match an helper
// function (e.g. $, keys, …).
matches = [...new Set(matches)].sort();
// Sort the results in order to display lowercased item first (e.g. we want to
// display `document` then `Document` as we loosely match the user input if the
// first letter they typed was lowercase).
matches = Array.from(matches).sort((a, b) => {
const lA = a[0].toLocaleLowerCase() === a[0];
const lB = b[0].toLocaleLowerCase() === b[0];
if (lA === lB) {
return a < b ? -1 : 1;
}
return lA ? -1 : 1;
});
}
return {
from: this.actorID,

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

@ -13,7 +13,6 @@ DevToolsModules(
'content-process-forward.js',
'eval-with-debugger.js',
'message-manager-mock.js',
'screenshot.js',
'utils.js',
'worker-listeners.js',
)

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

@ -7,11 +7,10 @@
const {Ci, Cu} = require("chrome");
loader.lazyRequireGetter(this, "screenshot", "devtools/server/actors/webconsole/screenshot", true);
// Note that this is only used in WebConsoleCommands, see $0 and pprint().
// Note that this is only used in WebConsoleCommands, see $0, screenshot and pprint().
if (!isWorker) {
loader.lazyImporter(this, "VariablesView", "resource://devtools/client/shared/widgets/VariablesView.jsm");
loader.lazyRequireGetter(this, "captureScreenshot", "devtools/shared/screenshot/capture", true);
}
const CONSOLE_WORKER_IDS = exports.CONSOLE_WORKER_IDS = [
@ -599,10 +598,11 @@ WebConsoleCommands._registerOriginal("copy", function(owner, value) {
* The arguments to be passed to the screenshot
* @return void
*/
WebConsoleCommands._registerOriginal("screenshot", function(owner, args) {
WebConsoleCommands._registerOriginal("screenshot", function(owner, args = {}) {
owner.helperResult = (async () => {
// creates data for saving the screenshot
const value = await screenshot(owner, args);
// help is handled on the client side
const value = await captureScreenshot(args, owner.window.document);
return {
type: "screenshotOutput",
value,

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

@ -426,6 +426,11 @@ var DebuggerServer = {
constructor: "AccessibilityActor",
type: { target: true }
});
this.registerModule("devtools/server/actors/screenshot", {
prefix: "screenshot",
constructor: "ScreenshotActor",
type: { target: true }
});
},
/**

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

@ -31,6 +31,7 @@ DevToolsModules(
'preference.js',
'promises.js',
'reflow.js',
'screenshot.js',
'storage.js',
'string.js',
'styles.js',

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

@ -0,0 +1,38 @@
/* 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 {screenshotSpec} = require("devtools/shared/specs/screenshot");
const saveScreenshot = require("devtools/shared/screenshot/save");
const protocol = require("devtools/shared/protocol");
const ScreenshotFront = protocol.FrontClassWithSpec(screenshotSpec, {
initialize: function(client, form) {
protocol.Front.prototype.initialize.call(this, client);
this.actorID = form.screenshotActor;
this.manage(this);
},
async captureAndSave(window, args) {
const screenshot = await this.capture(args);
return saveScreenshot(window, args, screenshot);
}
});
// A cache of created fronts: WeakMap<Client, Front>
const knownFronts = new WeakMap();
/**
* Create a screenshot front only when needed
*/
function getScreenshotFront(target) {
let front = knownFronts.get(target.client);
if (front == null && target.form.screenshotActor != null) {
front = new ScreenshotFront(target.client, target.form);
knownFronts.set(target.client, front);
}
return front;
}
exports.getScreenshotFront = getScreenshotFront;

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

@ -25,6 +25,7 @@ DIRS += [
'platform',
'pretty-fast',
'qrcode',
'screenshot',
'security',
'sourcemap',
'sprintfjs',

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

@ -3,25 +3,19 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
"use strict";
const { Cu } = require("chrome");
const { getRect } = require("devtools/shared/layout/utils");
const { LocalizationHelper } = require("devtools/shared/l10n");
const CONTAINER_FLASHING_DURATION = 500;
const STRINGS_URI = "devtools/shared/locales/screenshot.properties";
const L10N = new LocalizationHelper(STRINGS_URI);
exports.screenshot = function takeAsyncScreenshot(owner, args = {}) {
if (args.help) {
// Early return as help will be handled on the client side.
return null;
}
return captureScreenshot(args, owner.window.document);
};
loader.lazyRequireGetter(this, "getRect", "devtools/shared/layout/utils", true);
/**
* This function is called to simulate camera effects
* @param object document
* The target document.
*/
function simulateCameraFlash(document) {
const window = document.defaultView;
@ -34,21 +28,26 @@ function simulateCameraFlash(document) {
* createScreenshotData
*/
function captureScreenshot(args, document) {
if (args.help) {
return null;
}
if (args.delay > 0) {
return new Promise((resolve, reject) => {
document.defaultView.setTimeout(() => {
createScreenshotData(document, args).then(resolve, reject);
createScreenshotDataURL(document, args).then(resolve, reject);
}, args.delay * 1000);
});
}
return createScreenshotData(document, args);
return createScreenshotDataURL(document, args);
}
exports.captureScreenshot = captureScreenshot;
/**
* This does the dirty work of creating a base64 string out of an
* area of the browser window
*/
function createScreenshotData(document, args) {
function createScreenshotDataURL(document, args) {
const window = document.defaultView;
let left = 0;
let top = 0;
@ -111,6 +110,8 @@ function createScreenshotData(document, args) {
});
}
exports.createScreenshotDataURL = createScreenshotDataURL;
/**
* We may have a filename specified in args, or we might have to generate
* one.
@ -138,3 +139,4 @@ function getFilename(defaultName) {
timeString
) + ".png";
}

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

@ -0,0 +1,10 @@
# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
# vim: set filetype=python:
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
DevToolsModules(
'capture.js',
'save.js',
)

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

@ -98,8 +98,8 @@ function getFormattedHelpData() {
/**
* Main entry point in this file; Takes the original arguments that `:screenshot` was
* called with and the image value from the server, and uses the client window to save
* the screenshot to the remote debugging machine's memory or clipboard.
* called with and the image value from the server, and uses the client window to add
* and audio effect.
*
* @param object window
* The Debugger Client window.
@ -113,14 +113,14 @@ function getFormattedHelpData() {
* @return string[]
* Response messages from processing the screenshot
*/
function processScreenshot(window, args = {}, value) {
function saveScreenshot(window, args = {}, value) {
if (args.help) {
const message = getFormattedHelpData();
// Wrap meesage in an array so that the return value is consistant with saveScreenshot
// Wrap message in an array so that the return value is consistant with save
return [message];
}
simulateCameraShutter(window.document);
return saveScreenshot(window, args, value);
simulateCameraShutter(window);
return save(args, value);
}
/**
@ -129,8 +129,7 @@ function processScreenshot(window, args = {}, value) {
* @param object document
* The Debugger Client document.
*/
function simulateCameraShutter(document) {
const window = document.defaultView;
function simulateCameraShutter(window) {
if (Services.prefs.getBoolPref("devtools.screenshot.audio.enabled")) {
const audioCamera = new window.Audio("resource://devtools/client/themes/audio/shutter.wav");
audioCamera.play();
@ -140,9 +139,6 @@ function simulateCameraShutter(document) {
/**
* Save the captured screenshot to one of several destinations.
*
* @param object window
* The Debugger Client window.
*
* @param object args
* The original args with which the screenshot was called.
*
@ -152,18 +148,18 @@ function simulateCameraShutter(document) {
* @return string[]
* Response messages from processing the screenshot.
*/
async function saveScreenshot(window, args, image) {
async function save(args, image) {
const fileNeeded = args.filename ||
!args.clipboard || args.file;
const results = [];
if (args.clipboard) {
const result = saveToClipboard(window, image.data);
const result = saveToClipboard(image.data);
results.push(result);
}
if (fileNeeded) {
const result = await saveToFile(window, image);
const result = await saveToFile(image);
results.push(result);
}
return results;
@ -173,16 +169,13 @@ async function saveScreenshot(window, args, image) {
* Save the image data to the clipboard. This returns a promise, so it can
* be treated exactly like file processing.
*
* @param object window
* The Debugger Client window.
*
* @param string base64URI
* The image data encoded in a base64 URI that was sent from the server.
*
* @return string
* Response message from processing the screenshot.
*/
function saveToClipboard(window, base64URI) {
function saveToClipboard(base64URI) {
try {
const imageTools = Cc["@mozilla.org/image/tools;1"]
.getService(Ci.imgITools);
@ -212,16 +205,13 @@ function saveToClipboard(window, base64URI) {
* Save the screenshot data to disk, returning a promise which is resolved on
* completion.
*
* @param object window
* The Debugger Client window.
*
* @param object image
* The image object that was sent from the server.
*
* @return string
* Response message from processing the screenshot.
*/
async function saveToFile(window, image) {
async function saveToFile(image) {
let filename = image.filename;
// Check there is a .png extension to filename
@ -258,4 +248,4 @@ async function saveToFile(window, image) {
}
}
module.exports = processScreenshot;
module.exports = saveScreenshot;

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

@ -42,6 +42,7 @@ DevToolsModules(
'promises.js',
'property-iterator.js',
'reflow.js',
'screenshot.js',
'script.js',
'source.js',
'storage.js',

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

@ -0,0 +1,32 @@
/* 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 {RetVal, Arg, generateActorSpec, types} = require("devtools/shared/protocol");
types.addDictType("screenshot.args", {
fullpage: "nullable:boolean",
file: "nullable:boolean",
clipboard: "nullable:boolean",
selector: "nullable:string",
dpr: "nullable:string",
delay: "nullable:string"
});
const screenshotSpec = generateActorSpec({
typeName: "screenshot",
methods: {
capture: {
request: {
args: Arg(0, "screenshot.args")
},
response: {
value: RetVal("json")}
},
},
});
exports.screenshotSpec = screenshotSpec;

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

@ -180,7 +180,7 @@ function findCompletionBeginning(str) {
* If no completion valued could be computed, null is returned,
* otherwise a object with the following form is returned:
* {
* matches: [ string, string, string ],
* matches: Set<string>
* matchProp: Last part of the inputValue that was used to find
* the matches-strings.
* }
@ -407,17 +407,25 @@ function getMatchedProps(obj, match) {
* Get all properties in the given object (and its parent prototype chain) that
* match a given prefix.
*
* @param mixed obj
* @param {Mixed} obj
* Object whose properties we want to filter.
* @param string match
* @param {string} match
* Filter for properties that match this string.
* @return object
* Object that contains the matchProp and the list of names.
* @returns {object} which holds the following properties:
* - {string} matchProp.
* - {Set} matches: List of matched properties.
*/
function getMatchedPropsImpl(obj, match, {chainIterator, getProperties}) {
const matches = new Set();
let numProps = 0;
const insensitiveMatching = match && match[0].toUpperCase() !== match[0];
const propertyMatches = prop => {
return insensitiveMatching
? prop.toLocaleLowerCase().startsWith(match.toLocaleLowerCase())
: prop.startsWith(match);
};
// We need to go up the prototype chain.
const iter = chainIterator(obj);
for (obj of iter) {
@ -437,7 +445,7 @@ function getMatchedPropsImpl(obj, match, {chainIterator, getProperties}) {
for (let i = 0; i < props.length; i++) {
const prop = props[i];
if (prop.indexOf(match) != 0) {
if (!propertyMatches(prop)) {
continue;
}
if (prop.indexOf("-") > -1) {
@ -459,7 +467,7 @@ function getMatchedPropsImpl(obj, match, {chainIterator, getProperties}) {
return {
matchProp: match,
matches: [...matches],
matches,
};
}

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

@ -12,6 +12,5 @@ DevToolsModules(
'client.js',
'js-property-provider.js',
'network-helper.js',
'screenshot-helper.js',
'throttle.js',
)

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

@ -67,6 +67,14 @@
});
window.emojiObject = Object.create(null);
window.emojiObject["😎"] = "😎";
window.insensitiveTestCase = Object.create(null, Object.getOwnPropertyDescriptors({
PROP: "",
Prop: "",
prop: "",
PRÖP: "",
pröp: "",
}));
`;
await state.client.evaluateJSAsync(script);
@ -81,6 +89,7 @@
doAutocompleteProxyThrowsOwnKeys,
doAutocompleteDotSurroundedBySpaces,
doAutocompleteAfterOr,
doInsensitiveAutocomplete,
];
if (!isWorker) {
@ -182,7 +191,8 @@
ok(!response.matchProp, "matchProp");
let keys = Object.getOwnPropertyNames(Object.prototype).sort();
is(response.matches.length, keys.length, "matches.length");
checkObject(response.matches, keys);
// checkObject(response.matches, keys);
is(response.matches.join(" - "), keys.join(" - "));
}
async function doAutocompleteArray(client) {
@ -242,6 +252,41 @@
is(matches.length, 1, "autocomplete returns expected results");
is(matches.join("-"), "foobarObject");
}
async function doInsensitiveAutocomplete(client) {
info("test autocomplete for 'window.insensitiveTestCase.'");
let {matches} = await client.autocomplete("window.insensitiveTestCase.");
is(matches.join("-"), "prop-pröp-PROP-PRÖP-Prop",
"autocomplete returns the expected items, in the expected order");
info("test autocomplete for 'window.insensitiveTestCase.p'");
matches = (await client.autocomplete("window.insensitiveTestCase.p")).matches;
is(matches.join("-"), "prop-pröp-PROP-PRÖP-Prop",
"autocomplete is case-insensitive when first letter is lowercased");
info("test autocomplete for 'window.insensitiveTestCase.pRoP'");
matches = (await client.autocomplete("window.insensitiveTestCase.pRoP")).matches;
is(matches.join("-"), "prop-PROP-Prop",
"autocomplete is case-insensitive when first letter is lowercased");
info("test autocomplete for 'window.insensitiveTestCase.P'");
matches = (await client.autocomplete("window.insensitiveTestCase.P")).matches;
is(matches.join("-"), "PROP-PRÖP-Prop",
"autocomplete is case-sensitive when first letter is uppercased");
info("test autocomplete for 'window.insensitiveTestCase.PROP'");
matches = (await client.autocomplete("window.insensitiveTestCase.PROP")).matches;
is(matches.join("-"), "PROP",
"autocomplete is case-sensitive when first letter is uppercased");
info("test autocomplete for 'window.insensitiveTestCase.prö'");
matches = (await client.autocomplete("window.insensitiveTestCase.prö")).matches;
is(matches.join("-"), "pröp-PRÖP", "expected result with lowercase diacritic");
info("test autocomplete for 'window.insensitiveTestCase.PRÖ'");
matches = (await client.autocomplete("window.insensitiveTestCase.PRÖ")).matches;
is(matches.join("-"), "PRÖP", "expected result with uppercase diacritic");
}
</script>
</body>
</html>

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

@ -187,7 +187,7 @@ function runChecks(dbgObject, dbgEnv, sandbox) {
*/
function test_has_no_results(results) {
Assert.notEqual(results, null);
Assert.equal(results.matches.length, 0);
Assert.equal(results.matches.size, 0);
}
/**
* A helper that ensures (required) results were found.
@ -198,6 +198,6 @@ function test_has_no_results(results) {
*/
function test_has_result(results, requiredSuggestion) {
Assert.notEqual(results, null);
Assert.ok(results.matches.length > 0);
Assert.ok(results.matches.includes(requiredSuggestion));
Assert.ok(results.matches.size > 0);
Assert.ok(results.matches.has(requiredSuggestion));
}

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

@ -144,11 +144,8 @@ IDTracker::Reset(nsIContent* aFromContent,
}
void
IDTracker::ResetWithID(nsIContent* aFromContent,
nsAtom* aID,
bool aWatch)
IDTracker::ResetWithID(Element& aFrom, nsAtom* aID, bool aWatch)
{
MOZ_ASSERT(aFromContent);
MOZ_ASSERT(aID);
if (aWatch) {
@ -158,7 +155,7 @@ IDTracker::ResetWithID(nsIContent* aFromContent,
mReferencingImage = false;
DocumentOrShadowRoot* docOrShadow = DocOrShadowFromContent(*aFromContent);
DocumentOrShadowRoot* docOrShadow = DocOrShadowFromContent(aFrom);
HaveNewDocumentOrShadowRoot(docOrShadow, aWatch, nsDependentAtomString(aID));
}

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

@ -78,7 +78,7 @@ public:
* changes, so ElementChanged won't fire and get() will always return the same
* value, the current element for the ID.
*/
void ResetWithID(nsIContent* aFrom, nsAtom* aID, bool aWatch = true);
void ResetWithID(Element& aFrom, nsAtom* aID, bool aWatch = true);
/**
* Clears the reference. ElementChanged is not triggered. get() will return

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

@ -10042,6 +10042,7 @@ public:
parser->BlockParser();
mParser = do_GetWeakReference(parser);
mDocument = aDocument;
mDocument->BlockOnload();
}
}
@ -10080,6 +10081,7 @@ private:
if (parser == docParser) {
parser->UnblockParser();
parser->ContinueInterruptedParsingAsync();
mDocument->UnblockOnload(false);
}
}
mParser = nullptr;

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

@ -95,9 +95,11 @@ LOCAL_INCLUDES += [
'/media/webrtc/signaling/src/common/time_profiling',
'/media/webrtc/signaling/src/peerconnection',
'/media/webrtc/trunk/',
'/third_party/msgpack/include',
]
if CONFIG['MOZ_LIBPRIO']:
LOCAL_INCLUDES += ['/third_party/msgpack/include']
DEFINES['GOOGLE_PROTOBUF_NO_RTTI'] = True
DEFINES['GOOGLE_PROTOBUF_NO_STATIC_INITIALIZER'] = True

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

@ -44,7 +44,6 @@ WEBIDL_FILES = [
'MozStorageStatementParams.webidl',
'MozStorageStatementRow.webidl',
'PrecompiledScript.webidl',
'PrioEncoder.webidl',
'PromiseDebugging.webidl',
'StructuredCloneHolder.webidl',
'WebExtensionContentScript.webidl',
@ -58,3 +57,9 @@ if CONFIG['MOZ_PLACES']:
'PlacesEvent.webidl',
'PlacesObservers.webidl',
]
if CONFIG['MOZ_LIBPRIO']:
WEBIDL_FILES += [
'PrioEncoder.webidl',
]

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

@ -1003,6 +1003,20 @@ Event::DefaultPrevented(CallerType aCallerType) const
aCallerType == CallerType::System;
}
bool
Event::ReturnValue(CallerType aCallerType) const
{
return !DefaultPrevented(aCallerType);
}
void
Event::SetReturnValue(bool aReturnValue, CallerType aCallerType)
{
if (!aReturnValue) {
PreventDefaultInternal(aCallerType == CallerType::System);
}
}
double
Event::TimeStamp()
{

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

@ -281,6 +281,10 @@ public:
return mEvent->mFlags.mMultipleActionsPrevented;
}
bool ReturnValue(CallerType aCallerType) const;
void SetReturnValue(bool aReturnValue, CallerType aCallerType);
bool IsTrusted() const
{
return mEvent->IsTrusted();

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

@ -131,6 +131,7 @@ public:
virtual void SetDocumentCharset(NotNull<const Encoding*> aEncoding) override;
virtual nsISupports *GetTarget() override;
virtual bool IsScriptExecuting() override;
virtual void ContinueInterruptedParsingAsync() override;
// nsIHTMLContentSink
NS_IMETHOD OpenContainer(ElementType aNodeType) override;
@ -175,6 +176,9 @@ protected:
void NotifyInsert(nsIContent* aContent,
nsIContent* aChildContent);
void NotifyRootInsertion();
private:
void ContinueInterruptedParsingIfEnabled();
};
class SinkContext
@ -1047,3 +1051,23 @@ HTMLContentSink::IsScriptExecuting()
{
return IsScriptExecutingImpl();
}
void
HTMLContentSink::ContinueInterruptedParsingIfEnabled()
{
if (mParser->IsParserEnabled()) {
static_cast<nsIParser*>(mParser.get())->ContinueInterruptedParsing();
}
}
void
HTMLContentSink::ContinueInterruptedParsingAsync()
{
nsCOMPtr<nsIRunnable> ev =
NewRunnableMethod("HTMLContentSink::ContinueInterruptedParsingIfEnabled",
this,
&HTMLContentSink::ContinueInterruptedParsingIfEnabled);
nsCOMPtr<nsIDocument> doc = do_QueryInterface(mHTMLDocument);
doc->Dispatch(mozilla::TaskCategory::Other, ev.forget());
}

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

@ -63,7 +63,6 @@ DIRS += [
'notification',
'offline',
'power',
'prio',
'push',
'quota',
'security',
@ -105,6 +104,9 @@ DIRS += [
'simpledb',
]
if CONFIG['MOZ_LIBPRIO']:
DIRS += ['prio']
if CONFIG['OS_ARCH'] == 'WINNT':
DIRS += ['plugins/ipc/hangui']

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

@ -57,7 +57,7 @@ nsSMILTimeValueSpec::~nsSMILTimeValueSpec()
nsresult
nsSMILTimeValueSpec::SetSpec(const nsAString& aStringSpec,
Element* aContextNode)
Element& aContextElement)
{
nsSMILTimeValueSpecParams params;
@ -80,23 +80,21 @@ nsSMILTimeValueSpec::SetSpec(const nsAString& aStringSpec,
mParams.mEventSymbol = nsGkAtoms::repeatEvent;
}
ResolveReferences(aContextNode);
ResolveReferences(aContextElement);
return NS_OK;
}
void
nsSMILTimeValueSpec::ResolveReferences(nsIContent* aContextNode)
nsSMILTimeValueSpec::ResolveReferences(Element& aContextElement)
{
if (mParams.mType != nsSMILTimeValueSpecParams::SYNCBASE && !IsEventBased())
if (mParams.mType != nsSMILTimeValueSpecParams::SYNCBASE && !IsEventBased()) {
return;
MOZ_ASSERT(aContextNode,
"null context node for resolving timing references against");
}
// If we're not bound to the document yet, don't worry, we'll get called again
// when that happens
if (!aContextNode->IsInComposedDoc())
if (!aContextElement.IsInComposedDoc())
return;
// Hold ref to the old element so that it isn't destroyed in between resetting
@ -105,7 +103,7 @@ nsSMILTimeValueSpec::ResolveReferences(nsIContent* aContextNode)
RefPtr<Element> oldReferencedElement = mReferencedElement.get();
if (mParams.mDependentElemID) {
mReferencedElement.ResetWithID(aContextNode, mParams.mDependentElemID);
mReferencedElement.ResetWithID(aContextElement, mParams.mDependentElemID);
} else if (mParams.mType == nsSMILTimeValueSpecParams::EVENT) {
Element* target = mOwner->GetTargetElement();
mReferencedElement.ResetWithElement(target);

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

@ -48,9 +48,9 @@ public:
nsSMILTimeValueSpec(nsSMILTimedElement& aOwner, bool aIsBegin);
~nsSMILTimeValueSpec();
nsresult SetSpec(const nsAString& aStringSpec, Element* aContextNode);
void ResolveReferences(nsIContent* aContextNode);
bool IsEventBased() const;
nsresult SetSpec(const nsAString& aStringSpec, Element& aContextElement);
void ResolveReferences(Element& aContextElement);
bool IsEventBased() const;
void HandleNewInterval(nsSMILInterval& aInterval,
const nsSMILTimeContainer* aSrcContainer);

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

@ -853,20 +853,21 @@ namespace
} // namespace
bool
nsSMILTimedElement::SetAttr(nsAtom* aAttribute, const nsAString& aValue,
nsSMILTimedElement::SetAttr(nsAtom* aAttribute,
const nsAString& aValue,
nsAttrValue& aResult,
Element* aContextNode,
Element& aContextElement,
nsresult* aParseResult)
{
bool foundMatch = true;
nsresult parseResult = NS_OK;
if (aAttribute == nsGkAtoms::begin) {
parseResult = SetBeginSpec(aValue, aContextNode, RemoveNonDOM);
parseResult = SetBeginSpec(aValue, aContextElement, RemoveNonDOM);
} else if (aAttribute == nsGkAtoms::dur) {
parseResult = SetSimpleDuration(aValue);
} else if (aAttribute == nsGkAtoms::end) {
parseResult = SetEndSpec(aValue, aContextNode, RemoveNonDOM);
parseResult = SetEndSpec(aValue, aContextElement, RemoveNonDOM);
} else if (aAttribute == nsGkAtoms::fill) {
parseResult = SetFillMode(aValue);
} else if (aAttribute == nsGkAtoms::max) {
@ -928,10 +929,10 @@ nsSMILTimedElement::UnsetAttr(nsAtom* aAttribute)
nsresult
nsSMILTimedElement::SetBeginSpec(const nsAString& aBeginSpec,
Element* aContextNode,
Element& aContextElement,
RemovalTestFunction aRemove)
{
return SetBeginOrEndSpec(aBeginSpec, aContextNode, true /*isBegin*/,
return SetBeginOrEndSpec(aBeginSpec, aContextElement, true /*isBegin*/,
aRemove);
}
@ -944,10 +945,10 @@ nsSMILTimedElement::UnsetBeginSpec(RemovalTestFunction aRemove)
nsresult
nsSMILTimedElement::SetEndSpec(const nsAString& aEndSpec,
Element* aContextNode,
Element& aContextElement,
RemovalTestFunction aRemove)
{
return SetBeginOrEndSpec(aEndSpec, aContextNode, false /*!isBegin*/,
return SetBeginOrEndSpec(aEndSpec, aContextElement, false /*!isBegin*/,
aRemove);
}
@ -1205,7 +1206,7 @@ nsSMILTimedElement::IsTimeDependent(const nsSMILTimedElement& aOther) const
}
void
nsSMILTimedElement::BindToTree(nsIContent* aContextNode)
nsSMILTimedElement::BindToTree(Element& aContextElement)
{
// Reset previously registered milestone since we may be registering with
// a different time container now.
@ -1225,12 +1226,12 @@ nsSMILTimedElement::BindToTree(nsIContent* aContextNode)
// Resolve references to other parts of the tree
uint32_t count = mBeginSpecs.Length();
for (uint32_t i = 0; i < count; ++i) {
mBeginSpecs[i]->ResolveReferences(aContextNode);
mBeginSpecs[i]->ResolveReferences(aContextElement);
}
count = mEndSpecs.Length();
for (uint32_t j = 0; j < count; ++j) {
mEndSpecs[j]->ResolveReferences(aContextNode);
mEndSpecs[j]->ResolveReferences(aContextElement);
}
}
@ -1304,7 +1305,7 @@ nsSMILTimedElement::Unlink()
nsresult
nsSMILTimedElement::SetBeginOrEndSpec(const nsAString& aSpec,
Element* aContextNode,
Element& aContextElement,
bool aIsBegin,
RemovalTestFunction aRemove)
{
@ -1323,7 +1324,7 @@ nsSMILTimedElement::SetBeginOrEndSpec(const nsAString& aSpec,
bool hadFailure = false;
while (tokenizer.hasMoreTokens()) {
auto spec = MakeUnique<nsSMILTimeValueSpec>(*this, aIsBegin);
nsresult rv = spec->SetSpec(tokenizer.nextToken(), aContextNode);
nsresult rv = spec->SetSpec(tokenizer.nextToken(), aContextElement);
if (NS_SUCCEEDED(rv)) {
timeSpecsList.AppendElement(std::move(spec));
} else {
@ -1481,13 +1482,13 @@ nsSMILTimedElement::RebuildTimingState(RemovalTestFunction aRemove)
if (mAnimationElement->HasAttr(nsGkAtoms::begin)) {
nsAutoString attValue;
mAnimationElement->GetAttr(nsGkAtoms::begin, attValue);
SetBeginSpec(attValue, mAnimationElement, aRemove);
SetBeginSpec(attValue, *mAnimationElement, aRemove);
}
if (mAnimationElement->HasAttr(nsGkAtoms::end)) {
nsAutoString attValue;
mAnimationElement->GetAttr(nsGkAtoms::end, attValue);
SetEndSpec(attValue, mAnimationElement, aRemove);
SetEndSpec(attValue, *mAnimationElement, aRemove);
}
mPrevRegisteredMilestone = sMaxMilestone;

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

@ -60,7 +60,7 @@ public:
* Returns the element targeted by the animation element. Needed for
* registering event listeners against the appropriate element.
*/
mozilla::dom::Element* GetTargetElement();
Element* GetTargetElement();
/**
* Methods for supporting the ElementTimeControl interface.
@ -265,8 +265,8 @@ public:
* @param aValue The attribute value.
* @param aResult The nsAttrValue object that may be used for storing the
* parsed result.
* @param aContextNode The element to use for context when resolving
* references to other elements.
* @param aContextElement The element to use for context when resolving
* references to other elements.
* @param[out] aParseResult The result of parsing the attribute. Will be set
* to NS_OK if parsing is successful.
*
@ -274,8 +274,8 @@ public:
* otherwise.
*/
bool SetAttr(nsAtom* aAttribute, const nsAString& aValue,
nsAttrValue& aResult, Element* aContextNode,
nsresult* aParseResult = nullptr);
nsAttrValue& aResult, Element& aContextElement,
nsresult* aParseResult = nullptr);
/**
* Attempts to unset an attribute on this timed element.
@ -326,18 +326,17 @@ public:
* Called when the timed element has been bound to the document so that
* references from this timed element to other elements can be resolved.
*
* @param aContextNode The node which provides the necessary context for
* resolving references. This is typically the element in
* the host language that owns this timed element. Should
* not be null.
* @param aContextElement The element which provides the necessary context for
* resolving references. This is typically the element
* in the host language that owns this timed element.
*/
void BindToTree(nsIContent* aContextNode);
void BindToTree(Element& aContextElement);
/**
* Called when the target of the animation has changed so that event
* registrations can be updated.
*/
void HandleTargetElementChange(mozilla::dom::Element* aNewTarget);
void HandleTargetElementChange(Element* aNewTarget);
/**
* Called when the timed element has been removed from a document so that
@ -377,10 +376,10 @@ protected:
//
nsresult SetBeginSpec(const nsAString& aBeginSpec,
Element* aContextNode,
Element& aContextElement,
RemovalTestFunction aRemove);
nsresult SetEndSpec(const nsAString& aEndSpec,
Element* aContextNode,
Element& aContextElement,
RemovalTestFunction aRemove);
nsresult SetSimpleDuration(const nsAString& aDurSpec);
nsresult SetMin(const nsAString& aMinSpec);
@ -401,7 +400,7 @@ protected:
void UnsetFillMode();
nsresult SetBeginOrEndSpec(const nsAString& aSpec,
Element* aContextNode,
Element& aContextElement,
bool aIsBegin,
RemovalTestFunction aRemove);
void ClearSpecs(TimeValueSpecList& aSpecs,

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

@ -185,7 +185,7 @@ SVGAnimationElement::BindToTree(nsIDocument* aDocument,
UpdateHrefTarget(hrefStr);
}
mTimedElement.BindToTree(aParent);
mTimedElement.BindToTree(*this);
}
AnimationNeedsResample();
@ -234,7 +234,7 @@ SVGAnimationElement::ParseAttribute(int32_t aNamespaceID,
// try to parse it.
if (!foundMatch) {
foundMatch =
mTimedElement.SetAttr(aAttribute, aValue, aResult, this, &rv);
mTimedElement.SetAttr(aAttribute, aValue, aResult, *this, &rv);
}
if (foundMatch) {

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

@ -0,0 +1,11 @@
<script>
function start() {
document.replaceChild(id_0, document.childNodes[0]);
}
</script>
<body onload="start()">
<svg>
<animate id="id_0" begin="s" />
<ellipse id="id_1" />
</svg>
</body>

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