This commit is contained in:
Ryan VanderMeulen 2014-12-17 20:41:21 -05:00
Родитель 1ec6eba3c1 4601b67d6e
Коммит 311a052157
74 изменённых файлов: 1649 добавлений и 466 удалений

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

@ -14,6 +14,30 @@
%endif
}
/* These values are chosen to keep the Loop detached chat window from
* getting too small. When it's too small, three bad things happen:
*
* - It looks terrible
* - It's not really usable
* - It's possible for the user to be transmitting video that's cropped by the
* the edge of the window, so that they're not aware of it, which is a
* privacy problem
*
* Note that if the chat window grows more users than Loop who want this
* ability, we'll need to generalize. A partial patch for this is in
* bug 1112264.
*/
#chat-window {
/*
* In some ideal world, we'd have a simple way to express "block resizing
* along any dimension beyond the point at which an overflow event would
* occur". But none of -moz-{fit,max,min}-content do what we want here. So..
*/
min-width: 320px;
min-height: 280px;
}
#main-window[customize-entered] {
min-width: -moz-fit-content;
}
@ -849,7 +873,7 @@ toolbarbutton[type="socialmark"] {
toolbarbutton.badged-button > .toolbarbutton-badge-container > .toolbarbutton-icon,
toolbarbutton[type="socialmark"] > .toolbarbutton-icon {
max-width: 16px;
max-width: 18px;
}
toolbarpaletteitem[place="palette"] > toolbarbutton.badged-button > .toolbarbutton-badge-container > .toolbarbutton-icon {
max-width: 32px;

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

@ -95,8 +95,8 @@ addMessageListener("MixedContent:ReenableProtection", function() {
addMessageListener("SecondScreen:tab-mirror", function(message) {
let app = SimpleServiceDiscovery.findAppForService(message.data.service);
if (app) {
let width = content.scrollWidth;
let height = content.scrollHeight;
let width = content.innerWidth;
let height = content.innerHeight;
let viewport = {cssWidth: width, cssHeight: height, width: width, height: height};
app.mirror(function() {}, content, viewport, function() {}, content);
}

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

@ -1190,6 +1190,10 @@
// We need to explicitly focus the new tab, because
// tabbox.xml does this only in some cases.
this.mCurrentTab.focus();
} else if (gMultiProcessBrowser) {
// Clear focus so that _adjustFocusAfterTabSwitch can detect if
// some element has been focused and respect that.
document.activeElement.blur();
}
if (!gMultiProcessBrowser)
@ -1207,12 +1211,12 @@
<method name="_adjustFocusAfterTabSwitch">
<parameter name="newTab"/>
<body><![CDATA[
let newBrowser = this.getBrowserForTab(newTab);
// Don't steal focus from the tab bar.
if (document.activeElement == newTab)
return;
let newBrowser = this.getBrowserForTab(newTab);
// If there's a tabmodal prompt showing, focus it.
if (newBrowser.hasAttribute("tabmodalPromptShowing")) {
let XUL_NS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
@ -1226,7 +1230,6 @@
// In full screen mode, only bother making the location bar visible
// if the tab is a blank one.
if (newBrowser._urlbarFocused && gURLBar) {
// Explicitly close the popup if the URL bar retains focus
gURLBar.closePopup();
@ -1248,20 +1251,13 @@
return;
}
// Otherwise, focus the content area. If we're not using remote tabs, we
// can focus the content area right away, since tab switching is synchronous.
// If we're using remote tabs, we have to wait until after we've finalized
// switching the tabs.
if (newTab._skipContentFocus) {
// It's possible the tab we're switching to is ready to focus asynchronously,
// when we've already focused something else. In that case, this
// _skipContentFocus property can be set so that we skip focusing the
// content after we switch tabs.
delete newTab._skipContentFocus;
// Don't focus the content area if something has been focused after the
// tab switch was initiated.
if (gMultiProcessBrowser &&
document.activeElement != document.documentElement)
return;
}
// We're now committed to focusing the content area.
let fm = Services.focus;
let focusFlags = fm.FLAG_NOSCROLL;

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

@ -1035,17 +1035,17 @@
"searchbar-oneoffheader-searchtext");
let textbox = searchbar.textbox;
let self = this;
let keyPressHandler = function() {
let inputHandler = function() {
headerSearchText.setAttribute("value", textbox.value);
if (textbox.value)
self.removeAttribute("showonlysettings");
};
textbox.addEventListener("keyup", keyPressHandler);
textbox.addEventListener("input", inputHandler);
this.addEventListener("popuphiding", function hiding() {
textbox.removeEventListener("keyup", keyPressHandler);
textbox.removeEventListener("input", inputHandler);
this.removeEventListener("popuphiding", hiding);
});
keyPressHandler();
inputHandler();
// Handle opensearch items. This needs to be done before building the
// list of one off providers, as that code will return early if all the
@ -1060,8 +1060,6 @@
let addEngines = gBrowser.selectedBrowser.engines;
if (addEngines && addEngines.length > 0) {
const kBundleURI = "chrome://browser/locale/search.properties";
let bundle = Services.strings.createBundle(kBundleURI);
for (let engine of addEngines) {
let button = document.createElementNS(kXULNS, "button");
let label = bundle.formatStringFromName("cmd_addFoundEngine",

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

@ -309,8 +309,6 @@ function openLinkIn(url, where, params) {
// result in a new frontmost window (e.g. "javascript:window.open('');").
w.focus();
let newTab;
switch (where) {
case "current":
let flags = Ci.nsIWebNavigation.LOAD_FLAGS_NONE;
@ -333,7 +331,7 @@ function openLinkIn(url, where, params) {
loadInBackground = !loadInBackground;
// fall through
case "tab":
newTab = w.gBrowser.loadOneTab(url, {
w.gBrowser.loadOneTab(url, {
referrerURI: aReferrerURI,
charset: aCharset,
postData: aPostData,
@ -349,12 +347,6 @@ function openLinkIn(url, where, params) {
w.gBrowser.selectedBrowser.focus();
if (!loadInBackground && w.isBlankPageURL(url)) {
if (newTab && gMultiProcessBrowser) {
// Remote browsers are switched to asynchronously, and we need to
// ensure that the location bar remains focused in that case rather
// than the content area being focused.
newTab._skipContentFocus = true;
}
w.focusAndSelectUrlBar();
}
}

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

@ -570,6 +570,27 @@ this.LoopRooms = {
return LoopRoomsInternal.maybeRefresh(user);
},
/**
* This method is only useful for unit tests to set the rooms cache to contain
* a list of fake room data that can be asserted in tests.
*
* @param {Map} stub Stub cache containing fake rooms data
*/
stubCache: function(stub) {
LoopRoomsInternal.rooms.clear();
if (stub) {
// Fill up the rooms cache with room objects provided in the `stub` Map.
for (let [key, value] of stub.entries()) {
LoopRoomsInternal.rooms.set(key, value);
}
gDirty = false;
} else {
// Restore the cache to not be stubbed anymore, but it'll need a refresh
// from the server for sure.
gDirty = true;
}
},
promise: function(method, ...params) {
return new Promise((resolve, reject) => {
this[method](...params, (error, result) => {

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

@ -23,6 +23,8 @@ XPCOMUtils.defineLazyModuleGetter(this, "hookWindowCloseForPanelClose",
"resource://gre/modules/MozSocialAPI.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "PluralForm",
"resource://gre/modules/PluralForm.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "UITour",
"resource:///modules/UITour.jsm");
XPCOMUtils.defineLazyGetter(this, "appInfo", function() {
return Cc["@mozilla.org/xre/app-info;1"]
.getService(Ci.nsIXULAppInfo)
@ -735,7 +737,21 @@ function injectLoopAPI(targetWindow) {
callId: callid
});
}
}
},
/**
* Notifies the UITour module that an event occurred that it might be
* interested in.
*
* @param {String} subject Subject of the notification
*/
notifyUITour: {
enumerable: true,
writable: true,
value: function(subject) {
UITour.notify(subject);
}
},
};
function onStatusChanged(aSubject, aTopic, aData) {

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

@ -81,6 +81,16 @@ loop.roomViews = (function(mozL10n) {
this.stopListening(this.props.roomStore);
},
handleTextareaKeyDown: function(event) {
// Submit the form as soon as the user press Enter in that field
// Note: We're using a textarea instead of a simple text input to display
// placeholder and entered text on two lines, to circumvent l10n
// rendering/UX issues for some locales.
if (event.which === 13) {
this.handleFormSubmit(event);
}
},
handleFormSubmit: function(event) {
event.preventDefault();
@ -129,9 +139,10 @@ loop.roomViews = (function(mozL10n) {
mozL10n.get("rooms_name_change_failed_label")
),
React.DOM.form({onSubmit: this.handleFormSubmit},
React.DOM.input({type: "text", className: "input-room-name",
React.DOM.textarea({rows: "2", type: "text", className: "input-room-name",
valueLink: this.linkState("newRoomName"),
onBlur: this.handleFormSubmit,
onKeyDown: this.handleTextareaKeyDown,
placeholder: mozL10n.get("rooms_name_this_room_label")})
),
React.DOM.p(null, mozL10n.get("invite_header_text")),

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

@ -81,6 +81,16 @@ loop.roomViews = (function(mozL10n) {
this.stopListening(this.props.roomStore);
},
handleTextareaKeyDown: function(event) {
// Submit the form as soon as the user press Enter in that field
// Note: We're using a textarea instead of a simple text input to display
// placeholder and entered text on two lines, to circumvent l10n
// rendering/UX issues for some locales.
if (event.which === 13) {
this.handleFormSubmit(event);
}
},
handleFormSubmit: function(event) {
event.preventDefault();
@ -129,9 +139,10 @@ loop.roomViews = (function(mozL10n) {
{mozL10n.get("rooms_name_change_failed_label")}
</p>
<form onSubmit={this.handleFormSubmit}>
<input type="text" className="input-room-name"
<textarea rows="2" type="text" className="input-room-name"
valueLink={this.linkState("newRoomName")}
onBlur={this.handleFormSubmit}
onKeyDown={this.handleTextareaKeyDown}
placeholder={mozL10n.get("rooms_name_this_room_label")} />
</form>
<p>{mozL10n.get("invite_header_text")}</p>

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

@ -810,15 +810,20 @@ html, .fx-embedded, #main,
}
.room-invitation-overlay form {
padding: 8em 0 2.5em 0;
padding: 6em 0 2em 0;
}
.room-invitation-overlay input[type="text"] {
.room-invitation-overlay textarea {
display: block;
background: rgba(0, 0, 0, .5);
color: #fff;
font-family: "Helvetica Neue", Arial, sans;
font-size: 1.2em;
border: none;
width: 200px;
margin: 0 auto;
padding: .2em .4em;
border-radius: .5em;
}
.room-invitation-overlay .btn-group {

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

@ -298,6 +298,7 @@ loop.store = loop.store || {};
*/
copyRoomUrl: function(actionData) {
this._mozLoop.copyString(actionData.roomUrl);
this._mozLoop.notifyUITour("Loop:RoomURLCopied");
},
/**
@ -307,6 +308,7 @@ loop.store = loop.store || {};
*/
emailRoomUrl: function(actionData) {
loop.shared.utils.composeCallUrlEmail(actionData.roomUrl);
this._mozLoop.notifyUITour("Loop:RoomURLEmailed");
},
/**

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

@ -146,9 +146,9 @@ describe("loop.roomViews", function () {
}));
});
it("should dispatch a RenameRoom action when enter is pressed",
it("should dispatch a RenameRoom action when Enter key is pressed",
function() {
React.addons.TestUtils.Simulate.submit(roomNameBox);
TestUtils.Simulate.keyDown(roomNameBox, {key: "Enter", which: 13});
sinon.assert.calledOnce(dispatcher.dispatch);
sinon.assert.calledWithExactly(dispatcher.dispatch,

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

@ -77,6 +77,7 @@ describe("loop.store.RoomStore", function () {
beforeEach(function() {
fakeMozLoop = {
copyString: function() {},
notifyUITour: function() {},
rooms: {
create: function() {},
getAll: function() {},

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

@ -704,7 +704,11 @@
}
window.addEventListener("DOMContentLoaded", function() {
React.renderComponent(App(null), document.body);
try {
React.renderComponent(App(null), document.body);
} catch(err) {
console.log(err);
}
_renderComponentsInIframes();

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

@ -704,7 +704,11 @@
}
window.addEventListener("DOMContentLoaded", function() {
React.renderComponent(<App />, document.body);
try {
React.renderComponent(<App />, document.body);
} catch(err) {
console.log(err);
}
_renderComponentsInIframes();

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

@ -14,7 +14,7 @@
<?xml-stylesheet
href="chrome://browser/content/preferences/handlers.css"?>
<?xml-stylesheet href="chrome://browser/skin/preferences/applications.css"?>
<?xml-stylesheet href="chrome://browser/content/preferences/in-content/search.css"?>
<?xml-stylesheet href="chrome://browser/skin/preferences/in-content/search.css"?>
<!DOCTYPE page [
<!ENTITY % brandDTD SYSTEM "chrome://branding/locale/brand.dtd">

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

@ -4,6 +4,10 @@
Components.utils.import("resource://gre/modules/XPCOMUtils.jsm");
const ENGINE_FLAVOR = "text/x-moz-search-engine";
var gEngineView = null;
var gSearchPane = {
init: function ()
@ -15,9 +19,37 @@ var gSearchPane = {
return;
}
gEngineView = new EngineView(new EngineStore());
document.getElementById("engineList").view = gEngineView;
this.buildDefaultEngineDropDown();
Services.obs.addObserver(this, "browser-search-engine-modified", false);
window.addEventListener("unload", () => {
Services.obs.removeObserver(this, "browser-search-engine-modified", false);
});
},
buildDefaultEngineDropDown: function() {
// This is called each time something affects the list of engines.
let list = document.getElementById("defaultEngine");
let currentEngine = Services.search.currentEngine.name;
Services.search.getVisibleEngines().forEach(e => {
let currentEngine;
// First, try to preserve the current selection.
if (list.selectedItem)
currentEngine = list.selectedItem.label;
// If there's no current selection, use the current default engine.
if (!currentEngine)
currentEngine = Services.search.currentEngine.name;
// If the current engine isn't in the list any more, select the first item.
let engines = gEngineView._engineStore._engines;
if (!engines.some(e => e.name == currentEngine))
currentEngine = engines[0].name;
// Now clean-up and rebuild the list.
list.removeAllItems();
gEngineView._engineStore._engines.forEach(e => {
let item = list.appendItem(e.name);
item.setAttribute("class", "menuitem-iconic searchengine-menuitem menuitem-with-favicon");
if (e.iconURI)
@ -26,42 +58,119 @@ var gSearchPane = {
if (e.name == currentEngine)
list.selectedItem = item;
});
this.displayOneClickEnginesList();
document.getElementById("oneClickProvidersList")
.addEventListener("CheckboxStateChange", gSearchPane.saveOneClickEnginesList);
},
displayOneClickEnginesList: function () {
let richlistbox = document.getElementById("oneClickProvidersList");
let pref = document.getElementById("browser.search.hiddenOneOffs").value;
let hiddenList = pref ? pref.split(",") : [];
observe: function(aEngine, aTopic, aVerb) {
if (aTopic == "browser-search-engine-modified") {
aEngine.QueryInterface(Components.interfaces.nsISearchEngine);
switch (aVerb) {
case "engine-added":
gEngineView._engineStore.addEngine(aEngine);
gEngineView.rowCountChanged(gEngineView.lastIndex, 1);
gSearchPane.buildDefaultEngineDropDown();
break;
case "engine-changed":
gEngineView._engineStore.reloadIcons();
gEngineView.invalidate();
break;
case "engine-removed":
case "engine-current":
case "engine-default":
// Not relevant
break;
}
}
},
while (richlistbox.firstChild)
richlistbox.firstChild.remove();
onTreeSelect: function() {
document.getElementById("removeEngineButton").disabled =
gEngineView.selectedIndex == -1 || gEngineView.lastIndex == 0;
},
let currentEngine = Services.search.currentEngine.name;
Services.search.getVisibleEngines().forEach(e => {
if (e.name == currentEngine)
return;
onTreeKeyPress: function(aEvent) {
let index = gEngineView.selectedIndex;
let tree = document.getElementById("engineList");
if (aEvent.charCode == KeyEvent.DOM_VK_SPACE) {
// Space toggles the checkbox.
let newValue = !gEngineView._engineStore.engines[index].shown;
gEngineView.setCellValue(index, tree.columns.getFirstColumn(),
newValue.toString());
}
else {
let isMac = Services.appinfo.OS == "Darwin";
if ((isMac && aEvent.keyCode == KeyEvent.DOM_VK_RETURN) ||
(!isMac && aEvent.keyCode == KeyEvent.DOM_VK_F2))
tree.startEditing(index, tree.columns.getLastColumn());
}
},
let item = document.createElement("richlistitem");
item.setAttribute("label", e.name);
if (hiddenList.indexOf(e.name) == -1)
item.setAttribute("checked", "true");
if (e.iconURI)
item.setAttribute("src", e.iconURI.spec);
richlistbox.appendChild(item);
});
onRestoreDefaults: function() {
let num = gEngineView._engineStore.restoreDefaultEngines();
gEngineView.rowCountChanged(0, num);
gEngineView.invalidate();
},
showRestoreDefaults: function(aEnable) {
document.getElementById("restoreDefaultSearchEngines").disabled = !aEnable;
},
remove: function() {
gEngineView._engineStore.removeEngine(gEngineView.selectedEngine);
let index = gEngineView.selectedIndex;
gEngineView.rowCountChanged(index, -1);
gEngineView.invalidate();
gEngineView.selection.select(Math.min(index, gEngineView.lastIndex));
gEngineView.ensureRowIsVisible(gEngineView.currentIndex);
document.getElementById("engineList").focus();
},
editKeyword: function(aEngine, aNewKeyword) {
if (aNewKeyword) {
let bduplicate = false;
let eduplicate = false;
let dupName = "";
try {
let bmserv =
Components.classes["@mozilla.org/browser/nav-bookmarks-service;1"]
.getService(Components.interfaces.nsINavBookmarksService);
if (bmserv.getURIForKeyword(aNewKeyword))
bduplicate = true;
} catch(ex) {}
// Check for duplicates in changes we haven't committed yet
let engines = gEngineView._engineStore.engines;
for each (let engine in engines) {
if (engine.alias == aNewKeyword &&
engine.name != aEngine.name) {
eduplicate = true;
dupName = engine.name;
break;
}
}
// Notify the user if they have chosen an existing engine/bookmark keyword
if (eduplicate || bduplicate) {
let strings = document.getElementById("engineManagerBundle");
let dtitle = strings.getString("duplicateTitle");
let bmsg = strings.getString("duplicateBookmarkMsg");
let emsg = strings.getFormattedString("duplicateEngineMsg", [dupName]);
Services.prompt.alert(window, dtitle, eduplicate ? emsg : bmsg);
return false;
}
}
gEngineView._engineStore.changeEngine(aEngine, "alias", aNewKeyword);
gEngineView.invalidate();
return true;
},
saveOneClickEnginesList: function () {
let richlistbox = document.getElementById("oneClickProvidersList");
let hiddenList = [];
for (let child of richlistbox.childNodes) {
if (!child.checked)
hiddenList.push(child.getAttribute("label"));
for (let engine of gEngineView._engineStore.engines) {
if (!engine.shown)
hiddenList.push(engine.name);
}
document.getElementById("browser.search.hiddenOneOffs").value =
hiddenList.join(",");
@ -70,6 +179,273 @@ var gSearchPane = {
setDefaultEngine: function () {
Services.search.currentEngine =
document.getElementById("defaultEngine").selectedItem.engine;
this.displayOneClickEnginesList();
}
};
function onDragEngineStart(event) {
var selectedIndex = gEngineView.selectedIndex;
if (selectedIndex >= 0) {
event.dataTransfer.setData(ENGINE_FLAVOR, selectedIndex.toString());
event.dataTransfer.effectAllowed = "move";
}
}
function EngineStore() {
let pref = document.getElementById("browser.search.hiddenOneOffs").value;
this.hiddenList = pref ? pref.split(",") : [];
this._engines = Services.search.getVisibleEngines().map(this._cloneEngine, this);
this._defaultEngines = Services.search.getDefaultEngines().map(this._cloneEngine, this);
// check if we need to disable the restore defaults button
var someHidden = this._defaultEngines.some(function (e) e.hidden);
gSearchPane.showRestoreDefaults(someHidden);
}
EngineStore.prototype = {
_engines: null,
_defaultEngines: null,
get engines() {
return this._engines;
},
set engines(val) {
this._engines = val;
return val;
},
_getIndexForEngine: function ES_getIndexForEngine(aEngine) {
return this._engines.indexOf(aEngine);
},
_getEngineByName: function ES_getEngineByName(aName) {
for each (var engine in this._engines)
if (engine.name == aName)
return engine;
return null;
},
_cloneEngine: function ES_cloneEngine(aEngine) {
var clonedObj={};
for (var i in aEngine)
clonedObj[i] = aEngine[i];
clonedObj.originalEngine = aEngine;
clonedObj.shown = this.hiddenList.indexOf(clonedObj.name) == -1;
return clonedObj;
},
// Callback for Array's some(). A thisObj must be passed to some()
_isSameEngine: function ES_isSameEngine(aEngineClone) {
return aEngineClone.originalEngine == this.originalEngine;
},
addEngine: function ES_addEngine(aEngine) {
this._engines.push(this._cloneEngine(aEngine));
},
moveEngine: function ES_moveEngine(aEngine, aNewIndex) {
if (aNewIndex < 0 || aNewIndex > this._engines.length - 1)
throw new Error("ES_moveEngine: invalid aNewIndex!");
var index = this._getIndexForEngine(aEngine);
if (index == -1)
throw new Error("ES_moveEngine: invalid engine?");
if (index == aNewIndex)
return; // nothing to do
// Move the engine in our internal store
var removedEngine = this._engines.splice(index, 1)[0];
this._engines.splice(aNewIndex, 0, removedEngine);
Services.search.moveEngine(aEngine.originalEngine, aNewIndex);
},
removeEngine: function ES_removeEngine(aEngine) {
var index = this._getIndexForEngine(aEngine);
if (index == -1)
throw new Error("invalid engine?");
this._engines.splice(index, 1);
Services.search.removeEngine(aEngine.originalEngine);
if (this._defaultEngines.some(this._isSameEngine, aEngine))
gSearchPane.showRestoreDefaults(true);
gSearchPane.buildDefaultEngineDropDown();
},
restoreDefaultEngines: function ES_restoreDefaultEngines() {
var added = 0;
for (var i = 0; i < this._defaultEngines.length; ++i) {
var e = this._defaultEngines[i];
// If the engine is already in the list, just move it.
if (this._engines.some(this._isSameEngine, e)) {
this.moveEngine(this._getEngineByName(e.name), i);
} else {
// Otherwise, add it back to our internal store
this._engines.splice(i, 0, e);
let engine = e.originalEngine;
engine.hidden = false;
Services.search.moveEngine(engine, i);
added++;
}
}
gSearchPane.showRestoreDefaults(false);
gSearchPane.buildDefaultEngineDropDown();
return added;
},
changeEngine: function ES_changeEngine(aEngine, aProp, aNewValue) {
var index = this._getIndexForEngine(aEngine);
if (index == -1)
throw new Error("invalid engine?");
this._engines[index][aProp] = aNewValue;
aEngine.originalEngine[aProp] = aNewValue;
},
reloadIcons: function ES_reloadIcons() {
this._engines.forEach(function (e) {
e.uri = e.originalEngine.uri;
});
}
};
function EngineView(aEngineStore) {
this._engineStore = aEngineStore;
}
EngineView.prototype = {
_engineStore: null,
tree: null,
get lastIndex() {
return this.rowCount - 1;
},
get selectedIndex() {
var seln = this.selection;
if (seln.getRangeCount() > 0) {
var min = {};
seln.getRangeAt(0, min, {});
return min.value;
}
return -1;
},
get selectedEngine() {
return this._engineStore.engines[this.selectedIndex];
},
// Helpers
rowCountChanged: function (index, count) {
this.tree.rowCountChanged(index, count);
},
invalidate: function () {
this.tree.invalidate();
},
ensureRowIsVisible: function (index) {
this.tree.ensureRowIsVisible(index);
},
getSourceIndexFromDrag: function (dataTransfer) {
return parseInt(dataTransfer.getData(ENGINE_FLAVOR));
},
// nsITreeView
get rowCount() {
return this._engineStore.engines.length;
},
getImageSrc: function(index, column) {
if (column.id == "engineName" && this._engineStore.engines[index].iconURI)
return this._engineStore.engines[index].iconURI.spec;
return "";
},
getCellText: function(index, column) {
if (column.id == "engineName")
return this._engineStore.engines[index].name;
else if (column.id == "engineKeyword")
return this._engineStore.engines[index].alias;
return "";
},
setTree: function(tree) {
this.tree = tree;
},
canDrop: function(targetIndex, orientation, dataTransfer) {
var sourceIndex = this.getSourceIndexFromDrag(dataTransfer);
return (sourceIndex != -1 &&
sourceIndex != targetIndex &&
sourceIndex != targetIndex + orientation);
},
drop: function(dropIndex, orientation, dataTransfer) {
var sourceIndex = this.getSourceIndexFromDrag(dataTransfer);
var sourceEngine = this._engineStore.engines[sourceIndex];
const nsITreeView = Components.interfaces.nsITreeView;
if (dropIndex > sourceIndex) {
if (orientation == nsITreeView.DROP_BEFORE)
dropIndex--;
} else {
if (orientation == nsITreeView.DROP_AFTER)
dropIndex++;
}
this._engineStore.moveEngine(sourceEngine, dropIndex);
gSearchPane.showRestoreDefaults(true);
gSearchPane.buildDefaultEngineDropDown();
// Redraw, and adjust selection
this.invalidate();
this.selection.select(dropIndex);
},
selection: null,
getRowProperties: function(index) { return ""; },
getCellProperties: function(index, column) { return ""; },
getColumnProperties: function(column) { return ""; },
isContainer: function(index) { return false; },
isContainerOpen: function(index) { return false; },
isContainerEmpty: function(index) { return false; },
isSeparator: function(index) { return false; },
isSorted: function(index) { return false; },
getParentIndex: function(index) { return -1; },
hasNextSibling: function(parentIndex, index) { return false; },
getLevel: function(index) { return 0; },
getProgressMode: function(index, column) { },
getCellValue: function(index, column) {
if (column.id == "engineShown")
return this._engineStore.engines[index].shown;
return undefined;
},
toggleOpenState: function(index) { },
cycleHeader: function(column) { },
selectionChanged: function() { },
cycleCell: function(row, column) { },
isEditable: function(index, column) { return column.id != "engineName"; },
isSelectable: function(index, column) { return false; },
setCellValue: function(index, column, value) {
if (column.id == "engineShown") {
this._engineStore.engines[index].shown = value == "true";
gEngineView.invalidate();
gSearchPane.saveOneClickEnginesList();
}
},
setCellText: function(index, column, value) {
if (column.id == "engineKeyword") {
if (!gSearchPane.editKeyword(this._engineStore.engines[index], value)) {
setTimeout(() => {
document.getElementById("engineList").startEditing(index, column);
}, 0);
}
}
},
performAction: function(action) { },
performActionOnRow: function(action, index) { },
performActionOnCell: function(action, index, column) { }
};

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

@ -39,8 +39,34 @@
<caption label="&oneClickSearchEngines.label;"/>
<label>&chooseWhichOneToDisplay.label;</label>
<richlistbox id="oneClickProvidersList"/>
<hbox pack="end">
<tree id="engineList" flex="1" rows="8" hidecolumnpicker="true" editable="true"
seltype="single" onselect="gSearchPane.onTreeSelect();"
onkeypress="gSearchPane.onTreeKeyPress(event);">
<treechildren id="engineChildren" flex="1"
ondragstart="onDragEngineStart(event);"/>
<treecols>
<treecol id="engineShown" type="checkbox" style="min-width: 26px;" editable="true"/>
<treecol id="engineName" flex="4" label="&engineNameColumn.label;"/>
<treecol id="engineKeyword" flex="1" label="&engineKeywordColumn.label;" editable="true"/>
</treecols>
</tree>
<hbox>
<button id="restoreDefaultSearchEngines"
label="&restoreDefaultSearchEngines.label;"
accesskey="&restoreDefaultSearchEngines.accesskey;"
oncommand="gSearchPane.onRestoreDefaults();"/>
<spacer flex="1"/>
<button id="removeEngineButton"
label="&removeEngine.label;"
accesskey="&removeEngine.accesskey;"
disabled="true"
oncommand="gSearchPane.remove();"/>
</hbox>
<separator class="thin"/>
<hbox pack="start" style="margin-bottom: 1em">
<label id="addEngines" class="text-link" value="&addMoreSearchEngines.label;"
onclick="if (event.button == 0) { Services.wm.getMostRecentWindow('navigator:browser').BrowserSearch.loadAddEngines(); }"/>
</hbox>

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

@ -45,7 +45,6 @@ browser.jar:
content/browser/preferences/sync.js
#endif
content/browser/preferences/search.xul
content/browser/preferences/search.css
content/browser/preferences/search.js
* content/browser/preferences/tabs.xul
* content/browser/preferences/tabs.js

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

@ -16,7 +16,7 @@
-->
<?xml-stylesheet href="chrome://browser/content/preferences/handlers.css"?>
<?xml-stylesheet href="chrome://browser/skin/preferences/applications.css"?>
<?xml-stylesheet href="chrome://browser/content/preferences/search.css"?>
<?xml-stylesheet href="chrome://browser/skin/preferences/search.css"?>
<!DOCTYPE prefwindow [
<!ENTITY % brandDTD SYSTEM "chrome://branding/locale/brand.dtd">

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

@ -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/. */
#oneClickProvidersList richlistitem {
-moz-binding: url("chrome://global/content/bindings/checkbox.xml#checkbox");
-moz-padding-start: 5px;
height: 22px; /* setting the height of checkboxes is required to let the
window auto-sizing code work. */
}
#oneClickProvidersList {
height: 178px;
}
.searchengine-menuitem > .menu-iconic-left {
display: -moz-box
}
.checkbox-label-box {
-moz-box-align: center;
-moz-appearance: none;
border: none;
}
.checkbox-icon {
margin: 3px 3px;
max-width: 16px;
}

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

@ -4,13 +4,45 @@
Components.utils.import("resource://gre/modules/XPCOMUtils.jsm");
const ENGINE_FLAVOR = "text/x-moz-search-engine";
var gEngineView = null;
var gSearchPane = {
init: function ()
{
gEngineView = new EngineView(new EngineStore());
document.getElementById("engineList").view = gEngineView;
this.buildDefaultEngineDropDown();
Services.obs.addObserver(this, "browser-search-engine-modified", false);
window.addEventListener("unload", () => {
Services.obs.removeObserver(this, "browser-search-engine-modified", false);
});
},
buildDefaultEngineDropDown: function() {
// This is called each time something affects the list of engines.
let list = document.getElementById("defaultEngine");
let currentEngine = Services.search.currentEngine.name;
Services.search.getVisibleEngines().forEach(e => {
let currentEngine;
// First, try to preserve the current selection.
if (list.selectedItem)
currentEngine = list.selectedItem.label;
// If there's no current selection, use the current default engine.
if (!currentEngine)
currentEngine = Services.search.currentEngine.name;
// If the current engine isn't in the list any more, select the first item.
let engines = gEngineView._engineStore._engines;
if (!engines.some(e => e.name == currentEngine))
currentEngine = engines[0].name;
// Now clean-up and rebuild the list.
list.removeAllItems();
gEngineView._engineStore._engines.forEach(e => {
let item = list.appendItem(e.name);
item.setAttribute("class", "menuitem-iconic searchengine-menuitem menuitem-with-favicon");
if (e.iconURI)
@ -19,50 +51,472 @@ var gSearchPane = {
if (e.name == currentEngine)
list.selectedItem = item;
});
this.displayOneClickEnginesList();
document.getElementById("oneClickProvidersList")
.addEventListener("CheckboxStateChange", gSearchPane.saveOneClickEnginesList);
},
displayOneClickEnginesList: function () {
let richlistbox = document.getElementById("oneClickProvidersList");
let pref = document.getElementById("browser.search.hiddenOneOffs").value;
let hiddenList = pref ? pref.split(",") : [];
observe: function(aEngine, aTopic, aVerb) {
if (aTopic == "browser-search-engine-modified") {
aEngine.QueryInterface(Components.interfaces.nsISearchEngine);
switch (aVerb) {
case "engine-added":
gEngineView._engineStore.addEngine(aEngine);
gEngineView.rowCountChanged(gEngineView.lastIndex, 1);
gSearchPane.buildDefaultEngineDropDown();
break;
case "engine-changed":
gEngineView._engineStore.reloadIcons();
gEngineView.invalidate();
break;
case "engine-removed":
case "engine-current":
case "engine-default":
// Not relevant
break;
}
}
},
while (richlistbox.firstChild)
richlistbox.firstChild.remove();
onTreeSelect: function() {
document.getElementById("removeEngineButton").disabled =
gEngineView.selectedIndex == -1 || gEngineView.lastIndex == 0;
},
let currentEngine = Services.search.currentEngine.name;
Services.search.getVisibleEngines().forEach(e => {
if (e.name == currentEngine)
return;
onTreeKeyPress: function(aEvent) {
let index = gEngineView.selectedIndex;
let tree = document.getElementById("engineList");
if (aEvent.charCode == KeyEvent.DOM_VK_SPACE) {
// Space toggles the checkbox.
let newValue = !gEngineView._engineStore.engines[index].shown;
gEngineView.setCellValue(index, tree.columns.getFirstColumn(),
newValue.toString());
}
else {
let isMac = Services.appinfo.OS == "Darwin";
if ((isMac && aEvent.keyCode == KeyEvent.DOM_VK_RETURN) ||
(!isMac && aEvent.keyCode == KeyEvent.DOM_VK_F2))
tree.startEditing(index, tree.columns.getLastColumn());
}
},
let item = document.createElement("richlistitem");
item.setAttribute("label", e.name);
if (hiddenList.indexOf(e.name) == -1)
item.setAttribute("checked", "true");
if (e.iconURI)
item.setAttribute("src", e.iconURI.spec);
richlistbox.appendChild(item);
});
onRestoreDefaults: function() {
let num = gEngineView._engineStore.restoreDefaultEngines();
gEngineView.rowCountChanged(0, num);
gEngineView.invalidate();
},
showRestoreDefaults: function(aEnable) {
document.getElementById("restoreDefaultSearchEngines").disabled = !aEnable;
},
remove: function() {
gEngineView._engineStore.removeEngine(gEngineView.selectedEngine);
let index = gEngineView.selectedIndex;
gEngineView.rowCountChanged(index, -1);
gEngineView.invalidate();
gEngineView.selection.select(Math.min(index, gEngineView.lastIndex));
gEngineView.ensureRowIsVisible(gEngineView.currentIndex);
document.getElementById("engineList").focus();
},
editKeyword: function(aEngine, aNewKeyword) {
if (aNewKeyword) {
let bduplicate = false;
let eduplicate = false;
let dupName = "";
try {
let bmserv =
Components.classes["@mozilla.org/browser/nav-bookmarks-service;1"]
.getService(Components.interfaces.nsINavBookmarksService);
if (bmserv.getURIForKeyword(aNewKeyword))
bduplicate = true;
} catch(ex) {}
// Check for duplicates in changes we haven't committed yet
let engines = gEngineView._engineStore.engines;
for each (let engine in engines) {
if (engine.alias == aNewKeyword &&
engine.name != aEngine.name) {
eduplicate = true;
dupName = engine.name;
break;
}
}
// Notify the user if they have chosen an existing engine/bookmark keyword
if (eduplicate || bduplicate) {
let strings = document.getElementById("engineManagerBundle");
let dtitle = strings.getString("duplicateTitle");
let bmsg = strings.getString("duplicateBookmarkMsg");
let emsg = strings.getFormattedString("duplicateEngineMsg", [dupName]);
Services.prompt.alert(window, dtitle, eduplicate ? emsg : bmsg);
return false;
}
}
gEngineView._engineStore.changeEngine(aEngine, "alias", aNewKeyword);
gEngineView.invalidate();
return true;
},
saveOneClickEnginesList: function () {
let richlistbox = document.getElementById("oneClickProvidersList");
let hiddenList = [];
for (let child of richlistbox.childNodes) {
if (!child.checked)
hiddenList.push(child.getAttribute("label"));
for (let engine of gEngineView._engineStore.engines) {
if (!engine.shown)
hiddenList.push(engine.name);
}
document.getElementById("browser.search.hiddenOneOffs").value =
hiddenList.join(",");
},
setDefaultEngine: function () {
Services.search.currentEngine =
document.getElementById("defaultEngine").selectedItem.engine;
this.displayOneClickEnginesList();
if (document.documentElement.instantApply) {
Services.search.currentEngine =
document.getElementById("defaultEngine").selectedItem.engine;
}
}
};
function onDragEngineStart(event) {
var selectedIndex = gEngineView.selectedIndex;
if (selectedIndex >= 0) {
event.dataTransfer.setData(ENGINE_FLAVOR, selectedIndex.toString());
event.dataTransfer.effectAllowed = "move";
}
}
// "Operation" objects
function EngineMoveOp(aEngineClone, aNewIndex) {
if (!aEngineClone)
throw new Error("bad args to new EngineMoveOp!");
this._engine = aEngineClone.originalEngine;
this._newIndex = aNewIndex;
}
EngineMoveOp.prototype = {
_engine: null,
_newIndex: null,
commit: function EMO_commit() {
Services.search.moveEngine(this._engine, this._newIndex);
}
};
function EngineRemoveOp(aEngineClone) {
if (!aEngineClone)
throw new Error("bad args to new EngineRemoveOp!");
this._engine = aEngineClone.originalEngine;
}
EngineRemoveOp.prototype = {
_engine: null,
commit: function ERO_commit() {
Services.search.removeEngine(this._engine);
}
};
function EngineUnhideOp(aEngineClone, aNewIndex) {
if (!aEngineClone)
throw new Error("bad args to new EngineUnhideOp!");
this._engine = aEngineClone.originalEngine;
this._newIndex = aNewIndex;
}
EngineUnhideOp.prototype = {
_engine: null,
_newIndex: null,
commit: function EUO_commit() {
this._engine.hidden = false;
Services.search.moveEngine(this._engine, this._newIndex);
}
};
function EngineChangeOp(aEngineClone, aProp, aValue) {
if (!aEngineClone)
throw new Error("bad args to new EngineChangeOp!");
this._engine = aEngineClone.originalEngine;
this._prop = aProp;
this._newValue = aValue;
}
EngineChangeOp.prototype = {
_engine: null,
_prop: null,
_newValue: null,
commit: function ECO_commit() {
this._engine[this._prop] = this._newValue;
}
};
function EngineStore() {
let pref = document.getElementById("browser.search.hiddenOneOffs").value;
this.hiddenList = pref ? pref.split(",") : [];
this._engines = Services.search.getVisibleEngines().map(this._cloneEngine, this);
this._defaultEngines = Services.search.getDefaultEngines().map(this._cloneEngine, this);
if (document.documentElement.instantApply) {
this._ops = {
push: function(op) { op.commit(); }
};
}
else {
this._ops = [];
document.documentElement.addEventListener("beforeaccept", () => {
gEngineView._engineStore.commit();
});
}
// check if we need to disable the restore defaults button
var someHidden = this._defaultEngines.some(function (e) e.hidden);
gSearchPane.showRestoreDefaults(someHidden);
}
EngineStore.prototype = {
_engines: null,
_defaultEngines: null,
_ops: null,
get engines() {
return this._engines;
},
set engines(val) {
this._engines = val;
return val;
},
_getIndexForEngine: function ES_getIndexForEngine(aEngine) {
return this._engines.indexOf(aEngine);
},
_getEngineByName: function ES_getEngineByName(aName) {
for each (var engine in this._engines)
if (engine.name == aName)
return engine;
return null;
},
_cloneEngine: function ES_cloneEngine(aEngine) {
var clonedObj={};
for (var i in aEngine)
clonedObj[i] = aEngine[i];
clonedObj.originalEngine = aEngine;
clonedObj.shown = this.hiddenList.indexOf(clonedObj.name) == -1;
return clonedObj;
},
// Callback for Array's some(). A thisObj must be passed to some()
_isSameEngine: function ES_isSameEngine(aEngineClone) {
return aEngineClone.originalEngine == this.originalEngine;
},
commit: function ES_commit() {
for (op of this._ops)
op.commit();
Services.search.currentEngine =
document.getElementById("defaultEngine").selectedItem.engine;
},
addEngine: function ES_addEngine(aEngine) {
this._engines.push(this._cloneEngine(aEngine));
},
moveEngine: function ES_moveEngine(aEngine, aNewIndex) {
if (aNewIndex < 0 || aNewIndex > this._engines.length - 1)
throw new Error("ES_moveEngine: invalid aNewIndex!");
var index = this._getIndexForEngine(aEngine);
if (index == -1)
throw new Error("ES_moveEngine: invalid engine?");
if (index == aNewIndex)
return; // nothing to do
// Move the engine in our internal store
var removedEngine = this._engines.splice(index, 1)[0];
this._engines.splice(aNewIndex, 0, removedEngine);
this._ops.push(new EngineMoveOp(aEngine, aNewIndex));
},
removeEngine: function ES_removeEngine(aEngine) {
var index = this._getIndexForEngine(aEngine);
if (index == -1)
throw new Error("invalid engine?");
this._engines.splice(index, 1);
this._ops.push(new EngineRemoveOp(aEngine));
if (this._defaultEngines.some(this._isSameEngine, aEngine))
gSearchPane.showRestoreDefaults(true);
gSearchPane.buildDefaultEngineDropDown();
},
restoreDefaultEngines: function ES_restoreDefaultEngines() {
var added = 0;
for (var i = 0; i < this._defaultEngines.length; ++i) {
var e = this._defaultEngines[i];
// If the engine is already in the list, just move it.
if (this._engines.some(this._isSameEngine, e)) {
this.moveEngine(this._getEngineByName(e.name), i);
} else {
// Otherwise, add it back to our internal store
this._engines.splice(i, 0, e);
this._ops.push(new EngineUnhideOp(e, i));
added++;
}
}
gSearchPane.showRestoreDefaults(false);
gSearchPane.buildDefaultEngineDropDown();
return added;
},
changeEngine: function ES_changeEngine(aEngine, aProp, aNewValue) {
var index = this._getIndexForEngine(aEngine);
if (index == -1)
throw new Error("invalid engine?");
this._engines[index][aProp] = aNewValue;
this._ops.push(new EngineChangeOp(aEngine, aProp, aNewValue));
},
reloadIcons: function ES_reloadIcons() {
this._engines.forEach(function (e) {
e.uri = e.originalEngine.uri;
});
}
};
function EngineView(aEngineStore) {
this._engineStore = aEngineStore;
}
EngineView.prototype = {
_engineStore: null,
tree: null,
get lastIndex() {
return this.rowCount - 1;
},
get selectedIndex() {
var seln = this.selection;
if (seln.getRangeCount() > 0) {
var min = {};
seln.getRangeAt(0, min, {});
return min.value;
}
return -1;
},
get selectedEngine() {
return this._engineStore.engines[this.selectedIndex];
},
// Helpers
rowCountChanged: function (index, count) {
this.tree.rowCountChanged(index, count);
},
invalidate: function () {
this.tree.invalidate();
},
ensureRowIsVisible: function (index) {
this.tree.ensureRowIsVisible(index);
},
getSourceIndexFromDrag: function (dataTransfer) {
return parseInt(dataTransfer.getData(ENGINE_FLAVOR));
},
// nsITreeView
get rowCount() {
return this._engineStore.engines.length;
},
getImageSrc: function(index, column) {
if (column.id == "engineName" && this._engineStore.engines[index].iconURI)
return this._engineStore.engines[index].iconURI.spec;
return "";
},
getCellText: function(index, column) {
if (column.id == "engineName")
return this._engineStore.engines[index].name;
else if (column.id == "engineKeyword")
return this._engineStore.engines[index].alias;
return "";
},
setTree: function(tree) {
this.tree = tree;
},
canDrop: function(targetIndex, orientation, dataTransfer) {
var sourceIndex = this.getSourceIndexFromDrag(dataTransfer);
return (sourceIndex != -1 &&
sourceIndex != targetIndex &&
sourceIndex != targetIndex + orientation);
},
drop: function(dropIndex, orientation, dataTransfer) {
var sourceIndex = this.getSourceIndexFromDrag(dataTransfer);
var sourceEngine = this._engineStore.engines[sourceIndex];
const nsITreeView = Components.interfaces.nsITreeView;
if (dropIndex > sourceIndex) {
if (orientation == nsITreeView.DROP_BEFORE)
dropIndex--;
} else {
if (orientation == nsITreeView.DROP_AFTER)
dropIndex++;
}
this._engineStore.moveEngine(sourceEngine, dropIndex);
gSearchPane.showRestoreDefaults(true);
gSearchPane.buildDefaultEngineDropDown();
// Redraw, and adjust selection
this.invalidate();
this.selection.select(dropIndex);
},
selection: null,
getRowProperties: function(index) { return ""; },
getCellProperties: function(index, column) { return ""; },
getColumnProperties: function(column) { return ""; },
isContainer: function(index) { return false; },
isContainerOpen: function(index) { return false; },
isContainerEmpty: function(index) { return false; },
isSeparator: function(index) { return false; },
isSorted: function(index) { return false; },
getParentIndex: function(index) { return -1; },
hasNextSibling: function(parentIndex, index) { return false; },
getLevel: function(index) { return 0; },
getProgressMode: function(index, column) { },
getCellValue: function(index, column) {
if (column.id == "engineShown")
return this._engineStore.engines[index].shown;
return undefined;
},
toggleOpenState: function(index) { },
cycleHeader: function(column) { },
selectionChanged: function() { },
cycleCell: function(row, column) { },
isEditable: function(index, column) { return column.id != "engineName"; },
isSelectable: function(index, column) { return false; },
setCellValue: function(index, column, value) {
if (column.id == "engineShown") {
this._engineStore.engines[index].shown = value == "true";
gEngineView.invalidate();
gSearchPane.saveOneClickEnginesList();
}
},
setCellText: function(index, column, value) {
if (column.id == "engineKeyword") {
if (!gSearchPane.editKeyword(this._engineStore.engines[index], value)) {
setTimeout(() => {
document.getElementById("engineList").startEditing(index, column);
}, 0);
}
}
},
performAction: function(action) { },
performActionOnRow: function(action, index) { },
performActionOnCell: function(action, index, column) { }
};

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

@ -34,6 +34,8 @@
<script type="application/javascript" src="chrome://browser/content/preferences/search.js"/>
<stringbundle id="engineManagerBundle" src="chrome://browser/locale/engineManager.properties"/>
<!-- Default Search Engine -->
<groupbox id="defaultEngineGroup" align="start">
<caption label="&defaultSearchEngine.label;"/>
@ -51,8 +53,34 @@
<caption label="&oneClickSearchEngines.label;"/>
<label>&chooseWhichOneToDisplay.label;</label>
<richlistbox id="oneClickProvidersList"/>
<hbox pack="end" style="margin-bottom: 1em">
<tree id="engineList" flex="1" rows="8" hidecolumnpicker="true" editable="true"
seltype="single" onselect="gSearchPane.onTreeSelect();"
onkeypress="gSearchPane.onTreeKeyPress(event);">
<treechildren id="engineChildren" flex="1"
ondragstart="onDragEngineStart(event);"/>
<treecols>
<treecol id="engineShown" type="checkbox" style="min-width: 26px;" editable="true"/>
<treecol id="engineName" flex="4" label="&engineNameColumn.label;"/>
<treecol id="engineKeyword" flex="1" label="&engineKeywordColumn.label;" editable="true"/>
</treecols>
</tree>
<hbox>
<button id="restoreDefaultSearchEngines"
label="&restoreDefaultSearchEngines.label;"
accesskey="&restoreDefaultSearchEngines.accesskey;"
oncommand="gSearchPane.onRestoreDefaults();"/>
<spacer flex="1"/>
<button id="removeEngineButton"
label="&removeEngine.label;"
accesskey="&removeEngine.accesskey;"
disabled="true"
oncommand="gSearchPane.remove();"/>
</hbox>
<separator class="thin"/>
<hbox pack="start" style="margin-bottom: 1em">
<label id="addEngines" class="text-link" value="&addMoreSearchEngines.label;"
onclick="if (event.button == 0) { Services.wm.getMostRecentWindow('navigator:browser').BrowserSearch.loadAddEngines(); }"/>
</hbox>

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

@ -6,6 +6,7 @@ const {Cc, Ci, Cu} = require("chrome");
const {rgbToHsl} = require("devtools/css-color").colorUtils;
const {EventEmitter} = Cu.import("resource://gre/modules/devtools/event-emitter.js");
const promise = Cu.import("resource://gre/modules/Promise.jsm", {}).Promise;
const {setTimeout, clearTimeout} = Cu.import("resource://gre/modules/Timer.jsm", {});
Cu.import("resource://gre/modules/Services.jsm");
@ -546,7 +547,7 @@ Eyedropper.prototype = {
* Callback to be called when the color is in the clipboard.
*/
copyColor: function(callback) {
Services.appShell.hiddenDOMWindow.clearTimeout(this._copyTimeout);
clearTimeout(this._copyTimeout);
let color = this._colorValue.value;
clipboardHelper.copyString(color);
@ -554,7 +555,7 @@ Eyedropper.prototype = {
this._colorValue.classList.add("highlight");
this._colorValue.value = "✓ " + l10n.GetStringFromName("colorValue.copied");
this._copyTimeout = Services.appShell.hiddenDOMWindow.setTimeout(() => {
this._copyTimeout = setTimeout(() => {
this._colorValue.classList.remove("highlight");
this._colorValue.value = color;

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

@ -14,6 +14,7 @@ const promise = require("projecteditor/helpers/promise");
const { on, forget } = require("projecteditor/helpers/event");
const { FileResource } = require("projecteditor/stores/resource");
const {Services} = Cu.import("resource://gre/modules/Services.jsm");
const {setTimeout, clearTimeout} = Cu.import("resource://gre/modules/Timer.jsm", {});
const CHECK_LINKED_DIRECTORY_DELAY = 5000;
const SHOULD_LIVE_REFRESH = true;
@ -35,7 +36,6 @@ var LocalStore = Class({
initialize: function(path) {
this.initStore();
this.window = Services.appShell.hiddenDOMWindow;
this.path = OS.Path.normalize(path);
this.rootPath = this.path;
this.displayName = this.path;
@ -46,9 +46,8 @@ var LocalStore = Class({
},
destroy: function() {
if (this.window) {
this.window.clearTimeout(this._refreshTimeout);
}
clearTimeout(this._refreshTimeout);
if (this._refreshDeferred) {
this._refreshDeferred.reject("destroy");
}
@ -58,7 +57,6 @@ var LocalStore = Class({
this._refreshTimeout = null;
this._refreshDeferred = null;
this.window = null;
this.worker = null;
if (this.root) {
@ -132,7 +130,7 @@ var LocalStore = Class({
// XXX: Once Bug 958280 adds a watch function, will not need to forever loop here.
this.refresh().then(() => {
if (SHOULD_LIVE_REFRESH) {
this._refreshTimeout = this.window.setTimeout(this.refreshLoop,
this._refreshTimeout = setTimeout(this.refreshLoop,
CHECK_LINKED_DIRECTORY_DELAY);
}
});

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

@ -17,7 +17,7 @@ function test() {
waitForFocus(init, content);
}, true);
content.location = "data:text/html,browser_css_color.js";
content.location = "data:text/html;charset=utf-8,browser_css_color.js";
}
function init() {

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

@ -2822,32 +2822,10 @@ TextPropertyEditor.prototype = {
* Validate this property. Does it make sense for this value to be assigned
* to this property name? This does not apply the property value
*
* @param {string} [aValue]
* The property value used for validation.
* Defaults to the current value for this.prop
*
* @return {bool} true if the property value is valid, false otherwise.
*/
isValid: function(aValue) {
let name = this.prop.name;
let value = typeof aValue == "undefined" ? this.prop.value : aValue;
let val = parseSingleValue(value);
let style = this.doc.createElementNS(HTML_NS, "div").style;
let prefs = Services.prefs;
// We toggle output of errors whilst the user is typing a property value.
let prefVal = prefs.getBoolPref("layout.css.report_errors");
prefs.setBoolPref("layout.css.report_errors", false);
let validValue = false;
try {
style.setProperty(name, val.value, val.priority);
validValue = style.getPropertyValue(name) !== "" || val.value === "";
} finally {
prefs.setBoolPref("layout.css.report_errors", prefVal);
}
return validValue;
isValid: function() {
return domUtils.cssPropertyIsValid(this.prop.name, this.prop.value);
}
};

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

@ -1159,6 +1159,7 @@ this.UITour = {
let tooltipTitle = document.getElementById("UITourTooltipTitle");
let tooltipDesc = document.getElementById("UITourTooltipDescription");
let tooltipIcon = document.getElementById("UITourTooltipIcon");
let tooltipIconContainer = document.getElementById("UITourTooltipIconContainer");
let tooltipButtons = document.getElementById("UITourTooltipButtons");
if (tooltip.state == "showing" || tooltip.state == "open") {
@ -1168,7 +1169,7 @@ this.UITour = {
tooltipTitle.textContent = aTitle || "";
tooltipDesc.textContent = aDescription || "";
tooltipIcon.src = aIconURL || "";
tooltipIcon.hidden = !aIconURL;
tooltipIconContainer.hidden = !aIconURL;
while (tooltipButtons.firstChild)
tooltipButtons.firstChild.remove();

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

@ -1,160 +1,221 @@
/* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
let gTestTab;
let gContentAPI;
let gContentWindow;
let loopButton;
let loopPanel = document.getElementById("loop-notification-panel");
Components.utils.import("resource:///modules/UITour.jsm");
const { LoopRooms } = Components.utils.import("resource:///modules/loop/LoopRooms.jsm", {});
function test() {
UITourTest();
}
let tests = [
taskify(function* test_menu_show_hide() {
ise(loopButton.open, false, "Menu should initially be closed");
gContentAPI.showMenu("loop");
yield waitForConditionPromise(() => {
return loopButton.open;
}, "Menu should be visible after showMenu()");
ok(loopPanel.hasAttribute("noautohide"), "@noautohide should be on the loop panel");
ok(loopPanel.hasAttribute("panelopen"), "The panel should have @panelopen");
is(loopPanel.state, "open", "The panel should be open");
ok(loopButton.hasAttribute("open"), "Loop button should know that the menu is open");
gContentAPI.hideMenu("loop");
yield waitForConditionPromise(() => {
return !loopButton.open;
}, "Menu should be hidden after hideMenu()");
checkLoopPanelIsHidden();
}),
// Test the menu was cleaned up in teardown.
taskify(function* setup_menu_cleanup() {
gContentAPI.showMenu("loop");
yield waitForConditionPromise(() => {
return loopButton.open;
}, "Menu should be visible after showMenu()");
// Leave it open so it gets torn down and we can test below that teardown was succesful.
}),
taskify(function* test_menu_cleanup() {
// Test that the open menu from above was torn down fully.
checkLoopPanelIsHidden();
}),
function test_availableTargets(done) {
gContentAPI.showMenu("loop");
gContentAPI.getConfiguration("availableTargets", (data) => {
for (let targetName of ["loop-newRoom", "loop-roomList", "loop-signInUpLink"]) {
isnot(data.targets.indexOf(targetName), -1, targetName + " should exist");
}
done();
});
},
function test_hideMenuHidesAnnotations(done) {
let infoPanel = document.getElementById("UITourTooltip");
let highlightPanel = document.getElementById("UITourHighlightContainer");
gContentAPI.showMenu("loop", function menuCallback() {
gContentAPI.showHighlight("loop-roomList");
gContentAPI.showInfo("loop-newRoom", "Make a new room", "AKA. conversation");
UITour.getTarget(window, "loop-newRoom").then((target) => {
waitForPopupAtAnchor(infoPanel, target.node, Task.async(function* checkPanelIsOpen() {
isnot(loopPanel.state, "closed", "Loop panel should still be open");
ok(loopPanel.hasAttribute("noautohide"), "@noautohide should still be on the loop panel");
is(highlightPanel.getAttribute("targetName"), "loop-roomList", "Check highlight @targetname");
is(infoPanel.getAttribute("targetName"), "loop-newRoom", "Check info panel @targetname");
info("Close the loop menu and make sure the annotations inside disappear");
let hiddenPromises = [promisePanelElementHidden(window, infoPanel),
promisePanelElementHidden(window, highlightPanel)];
gContentAPI.hideMenu("loop");
yield Promise.all(hiddenPromises);
isnot(infoPanel.state, "open", "Info panel should have automatically hid");
isnot(highlightPanel.state, "open", "Highlight panel should have automatically hid");
done();
}), "Info panel should be anchored to the new room button");
});
});
},
function test_notifyLoopChatWindowOpenedClosed(done) {
gContentAPI.observe((event, params) => {
is(event, "Loop:ChatWindowOpened", "Check Loop:ChatWindowOpened notification");
gContentAPI.observe((event, params) => {
is(event, "Loop:ChatWindowShown", "Check Loop:ChatWindowShown notification");
gContentAPI.observe((event, params) => {
is(event, "Loop:ChatWindowClosed", "Check Loop:ChatWindowClosed notification");
gContentAPI.observe((event, params) => {
ok(false, "No more notifications should have arrived");
});
});
done();
});
document.querySelector("#pinnedchats > chatbox").close();
});
LoopRooms.open("fakeTourRoom");
},
taskify(function* test_arrow_panel_position() {
ise(loopButton.open, false, "Menu should initially be closed");
let popup = document.getElementById("UITourTooltip");
yield showMenuPromise("loop");
let currentTarget = "loop-newRoom";
yield showInfoPromise(currentTarget, "This is " + currentTarget, "My arrow should be on the side");
is(popup.popupBoxObject.alignmentPosition, "start_before", "Check " + currentTarget + " position");
currentTarget = "loop-roomList";
yield showInfoPromise(currentTarget, "This is " + currentTarget, "My arrow should be on the side");
is(popup.popupBoxObject.alignmentPosition, "start_before", "Check " + currentTarget + " position");
currentTarget = "loop-signInUpLink";
yield showInfoPromise(currentTarget, "This is " + currentTarget, "My arrow should be underneath");
is(popup.popupBoxObject.alignmentPosition, "after_end", "Check " + currentTarget + " position");
}),
];
// End tests
function checkLoopPanelIsHidden() {
ok(!loopPanel.hasAttribute("noautohide"), "@noautohide on the loop panel should have been cleaned up");
ok(!loopPanel.hasAttribute("panelopen"), "The panel shouldn't have @panelopen");
isnot(loopPanel.state, "open", "The panel shouldn't be open");
is(loopButton.hasAttribute("open"), false, "Loop button should know that the panel is closed");
}
if (Services.prefs.getBoolPref("loop.enabled")) {
loopButton = window.LoopUI.toolbarButton.node;
// The targets to highlight only appear after getting started is launched.
Services.prefs.setBoolPref("loop.gettingStarted.seen", true);
Services.prefs.setCharPref("loop.server", "http://localhost");
Services.prefs.setCharPref("services.push.serverURL", "ws://localhost/");
registerCleanupFunction(() => {
Services.prefs.clearUserPref("loop.gettingStarted.seen");
Services.prefs.clearUserPref("loop.server");
Services.prefs.clearUserPref("services.push.serverURL");
// Copied from browser/components/loop/test/mochitest/head.js
// Remove the iframe after each test. This also avoids mochitest complaining
// about leaks on shutdown as we intentionally hold the iframe open for the
// life of the application.
let frameId = loopButton.getAttribute("notificationFrameId");
let frame = document.getElementById(frameId);
if (frame) {
frame.remove();
}
});
} else {
ok(true, "Loop is disabled so skip the UITour Loop tests");
tests = [];
}
/* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
let gTestTab;
let gContentAPI;
let gContentWindow;
let loopButton;
let loopPanel = document.getElementById("loop-notification-panel");
Components.utils.import("resource:///modules/UITour.jsm");
const { LoopRooms } = Components.utils.import("resource:///modules/loop/LoopRooms.jsm", {});
function test() {
UITourTest();
}
let tests = [
taskify(function* test_menu_show_hide() {
ise(loopButton.open, false, "Menu should initially be closed");
gContentAPI.showMenu("loop");
yield waitForConditionPromise(() => {
return loopButton.open;
}, "Menu should be visible after showMenu()");
ok(loopPanel.hasAttribute("noautohide"), "@noautohide should be on the loop panel");
ok(loopPanel.hasAttribute("panelopen"), "The panel should have @panelopen");
is(loopPanel.state, "open", "The panel should be open");
ok(loopButton.hasAttribute("open"), "Loop button should know that the menu is open");
gContentAPI.hideMenu("loop");
yield waitForConditionPromise(() => {
return !loopButton.open;
}, "Menu should be hidden after hideMenu()");
checkLoopPanelIsHidden();
}),
// Test the menu was cleaned up in teardown.
taskify(function* setup_menu_cleanup() {
gContentAPI.showMenu("loop");
yield waitForConditionPromise(() => {
return loopButton.open;
}, "Menu should be visible after showMenu()");
// Leave it open so it gets torn down and we can test below that teardown was succesful.
}),
taskify(function* test_menu_cleanup() {
// Test that the open menu from above was torn down fully.
checkLoopPanelIsHidden();
}),
function test_availableTargets(done) {
gContentAPI.showMenu("loop");
gContentAPI.getConfiguration("availableTargets", (data) => {
for (let targetName of ["loop-newRoom", "loop-roomList", "loop-signInUpLink"]) {
isnot(data.targets.indexOf(targetName), -1, targetName + " should exist");
}
done();
});
},
function test_hideMenuHidesAnnotations(done) {
let infoPanel = document.getElementById("UITourTooltip");
let highlightPanel = document.getElementById("UITourHighlightContainer");
gContentAPI.showMenu("loop", function menuCallback() {
gContentAPI.showHighlight("loop-roomList");
gContentAPI.showInfo("loop-newRoom", "Make a new room", "AKA. conversation");
UITour.getTarget(window, "loop-newRoom").then((target) => {
waitForPopupAtAnchor(infoPanel, target.node, Task.async(function* checkPanelIsOpen() {
isnot(loopPanel.state, "closed", "Loop panel should still be open");
ok(loopPanel.hasAttribute("noautohide"), "@noautohide should still be on the loop panel");
is(highlightPanel.getAttribute("targetName"), "loop-roomList", "Check highlight @targetname");
is(infoPanel.getAttribute("targetName"), "loop-newRoom", "Check info panel @targetname");
info("Close the loop menu and make sure the annotations inside disappear");
let hiddenPromises = [promisePanelElementHidden(window, infoPanel),
promisePanelElementHidden(window, highlightPanel)];
gContentAPI.hideMenu("loop");
yield Promise.all(hiddenPromises);
isnot(infoPanel.state, "open", "Info panel should have automatically hid");
isnot(highlightPanel.state, "open", "Highlight panel should have automatically hid");
done();
}), "Info panel should be anchored to the new room button");
});
});
},
function test_notifyLoopChatWindowOpenedClosed(done) {
gContentAPI.observe((event, params) => {
is(event, "Loop:ChatWindowOpened", "Check Loop:ChatWindowOpened notification");
gContentAPI.observe((event, params) => {
is(event, "Loop:ChatWindowShown", "Check Loop:ChatWindowShown notification");
gContentAPI.observe((event, params) => {
is(event, "Loop:ChatWindowClosed", "Check Loop:ChatWindowClosed notification");
gContentAPI.observe((event, params) => {
ok(false, "No more notifications should have arrived");
});
});
done();
});
document.querySelector("#pinnedchats > chatbox").close();
});
LoopRooms.open("fakeTourRoom");
},
function test_notifyLoopRoomURLCopied(done) {
gContentAPI.observe((event, params) => {
is(event, "Loop:ChatWindowOpened", "Loop chat window should've opened");
gContentAPI.observe((event, params) => {
is(event, "Loop:ChatWindowShown", "Check Loop:ChatWindowShown notification");
let chat = document.querySelector("#pinnedchats > chatbox");
gContentAPI.observe((event, params) => {
is(event, "Loop:RoomURLCopied", "Check Loop:RoomURLCopied notification");
gContentAPI.observe((event, params) => {
is(event, "Loop:ChatWindowClosed", "Check Loop:ChatWindowClosed notification");
});
chat.close();
done();
});
chat.content.contentDocument.querySelector(".btn-copy").click();
});
});
setupFakeRoom();
LoopRooms.open("fakeTourRoom");
},
function test_notifyLoopRoomURLEmailed(done) {
gContentAPI.observe((event, params) => {
is(event, "Loop:ChatWindowOpened", "Loop chat window should've opened");
gContentAPI.observe((event, params) => {
is(event, "Loop:ChatWindowShown", "Check Loop:ChatWindowShown notification");
let chat = document.querySelector("#pinnedchats > chatbox");
let composeEmailCalled = false;
gContentAPI.observe((event, params) => {
is(event, "Loop:RoomURLEmailed", "Check Loop:RoomURLEmailed notification");
ok(composeEmailCalled, "mozLoop.composeEmail should be called");
gContentAPI.observe((event, params) => {
is(event, "Loop:ChatWindowClosed", "Check Loop:ChatWindowClosed notification");
});
chat.close();
done();
});
let chatWin = chat.content.contentWindow;
let oldComposeEmail = chatWin.navigator.wrappedJSObject.mozLoop.composeEmail;
chatWin.navigator.wrappedJSObject.mozLoop.composeEmail = function(recipient, subject, body) {
ok(recipient, "composeEmail should be invoked with at least a recipient value");
composeEmailCalled = true;
chatWin.navigator.wrappedJSObject.mozLoop.composeEmail = oldComposeEmail;
};
chatWin.document.querySelector(".btn-email").click();
});
});
LoopRooms.open("fakeTourRoom");
},
taskify(function* test_arrow_panel_position() {
ise(loopButton.open, false, "Menu should initially be closed");
let popup = document.getElementById("UITourTooltip");
yield showMenuPromise("loop");
let currentTarget = "loop-newRoom";
yield showInfoPromise(currentTarget, "This is " + currentTarget, "My arrow should be on the side");
is(popup.popupBoxObject.alignmentPosition, "start_before", "Check " + currentTarget + " position");
currentTarget = "loop-roomList";
yield showInfoPromise(currentTarget, "This is " + currentTarget, "My arrow should be on the side");
is(popup.popupBoxObject.alignmentPosition, "start_before", "Check " + currentTarget + " position");
currentTarget = "loop-signInUpLink";
yield showInfoPromise(currentTarget, "This is " + currentTarget, "My arrow should be underneath");
is(popup.popupBoxObject.alignmentPosition, "after_end", "Check " + currentTarget + " position");
}),
];
// End tests
function checkLoopPanelIsHidden() {
ok(!loopPanel.hasAttribute("noautohide"), "@noautohide on the loop panel should have been cleaned up");
ok(!loopPanel.hasAttribute("panelopen"), "The panel shouldn't have @panelopen");
isnot(loopPanel.state, "open", "The panel shouldn't be open");
is(loopButton.hasAttribute("open"), false, "Loop button should know that the panel is closed");
}
function setupFakeRoom() {
let room = {};
for (let prop of ["roomToken", "roomName", "roomOwner", "roomUrl", "participants"])
room[prop] = "fakeTourRoom";
LoopRooms.stubCache(new Map([
[room.roomToken, room]
]));
}
if (Services.prefs.getBoolPref("loop.enabled")) {
loopButton = window.LoopUI.toolbarButton.node;
// The targets to highlight only appear after getting started is launched.
Services.prefs.setBoolPref("loop.gettingStarted.seen", true);
Services.prefs.setCharPref("loop.server", "http://localhost");
Services.prefs.setCharPref("services.push.serverURL", "ws://localhost/");
registerCleanupFunction(() => {
Services.prefs.clearUserPref("loop.gettingStarted.seen");
Services.prefs.clearUserPref("loop.server");
Services.prefs.clearUserPref("services.push.serverURL");
// Copied from browser/components/loop/test/mochitest/head.js
// Remove the iframe after each test. This also avoids mochitest complaining
// about leaks on shutdown as we intentionally hold the iframe open for the
// life of the application.
let frameId = loopButton.getAttribute("notificationFrameId");
let frame = document.getElementById(frameId);
if (frame) {
frame.remove();
}
});
} else {
ok(true, "Loop is disabled so skip the UITour Loop tests");
tests = [];
}

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

@ -167,8 +167,12 @@ browser.jar:
skin/classic/browser/preferences/in-content/favicon.ico (../shared/incontentprefs/favicon.ico)
skin/classic/browser/preferences/in-content/icons.png (../shared/incontentprefs/icons.png)
skin/classic/browser/preferences/in-content/icons@2x.png (../shared/incontentprefs/icons@2x.png)
skin/classic/browser/preferences/in-content/search.css (../shared/incontentprefs/search.css)
skin/classic/browser/preferences/in-content/check.png (../shared/incontentprefs/check.png)
skin/classic/browser/preferences/in-content/check@2x.png (../shared/incontentprefs/check@2x.png)
skin/classic/browser/preferences/applications.css (preferences/applications.css)
skin/classic/browser/preferences/aboutPermissions.css (preferences/aboutPermissions.css)
skin/classic/browser/preferences/search.css (preferences/search.css)
skin/classic/browser/social/services-16.png (social/services-16.png)
skin/classic/browser/social/services-64.png (social/services-64.png)
skin/classic/browser/social/share-button.png (social/share-button.png)

Двоичные данные
browser/themes/linux/loop/menuPanel.png

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

До

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

После

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

Двоичные данные
browser/themes/linux/loop/toolbar-inverted.png

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

До

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

После

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

Двоичные данные
browser/themes/linux/loop/toolbar.png

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

До

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

После

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

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

@ -0,0 +1,39 @@
/* 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/. */
#defaultEngine > .menulist-label-box > .menulist-icon {
height: 16px;
}
/* work around a display: none in Linux's menu.css, see bug 1112310 */
.searchengine-menuitem > .menu-iconic-left {
display: -moz-box;
}
#engineList {
margin: .5em 6px;
}
#engineList treechildren::-moz-tree-checkbox(checked) {
list-style-image: url("chrome://global/skin/checkbox/cbox-check.gif");
}
#engineList treechildren::-moz-tree-image(engineName) {
-moz-margin-end: 4px;
-moz-margin-start: 0;
width: 16px;
height: 16px;
}
#engineList treechildren::-moz-tree-row {
min-height: 20px;
}
#engineList treechildren::-moz-tree-drop-feedback {
background-color: Highlight;
width: 10000px; /* 100% doesn't work; 10k is hopefully larger than any window
we may have, overflow isn't visible. */
height: 2px;
-moz-margin-start: 0;
}

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

@ -271,8 +271,16 @@ browser.jar:
skin/classic/browser/preferences/in-content/favicon.ico (../shared/incontentprefs/favicon.ico)
skin/classic/browser/preferences/in-content/icons.png (../shared/incontentprefs/icons.png)
skin/classic/browser/preferences/in-content/icons@2x.png (../shared/incontentprefs/icons@2x.png)
skin/classic/browser/preferences/in-content/search.css (../shared/incontentprefs/search.css)
skin/classic/browser/preferences/in-content/check.png (../shared/incontentprefs/check.png)
skin/classic/browser/preferences/in-content/check@2x.png (../shared/incontentprefs/check@2x.png)
skin/classic/browser/preferences/applications.css (preferences/applications.css)
skin/classic/browser/preferences/aboutPermissions.css (preferences/aboutPermissions.css)
skin/classic/browser/preferences/search.css (preferences/search.css)
skin/classic/browser/preferences/checkbox.png (preferences/checkbox.png)
skin/classic/browser/preferences/checkbox@2x.png (preferences/checkbox@2x.png)
skin/classic/browser/yosemite/preferences/checkbox.png (preferences/checkbox-yosemite.png)
skin/classic/browser/yosemite/preferences/checkbox@2x.png (preferences/checkbox-yosemite@2x.png)
skin/classic/browser/social/services-16.png (social/services-16.png)
skin/classic/browser/social/services-16@2x.png (social/services-16@2x.png)
skin/classic/browser/social/services-64.png (social/services-64.png)
@ -588,6 +596,8 @@ browser.jar:
% override chrome://browser/skin/menuPanel-help@2x.png chrome://browser/skin/yosemite/menuPanel-help@2x.png os=Darwin osversion>=10.10
% override chrome://browser/skin/menuPanel-small.png chrome://browser/skin/yosemite/menuPanel-small.png os=Darwin osversion>=10.10
% override chrome://browser/skin/menuPanel-small@2x.png chrome://browser/skin/yosemite/menuPanel-small@2x.png os=Darwin osversion>=10.10
% override chrome://browser/skin/preferences/checkbox.png chrome://browser/skin/yosemite/preferences/checkbox.png os=Darwin osversion>=10.10
% override chrome://browser/skin/preferences/checkbox@2x.png chrome://browser/skin/yosemite/preferences/checkbox@2x.png os=Darwin osversion>=10.10
% override chrome://browser/skin/reload-stop-go.png chrome://browser/skin/yosemite/reload-stop-go.png os=Darwin osversion>=10.10
% override chrome://browser/skin/reload-stop-go@2x.png chrome://browser/skin/yosemite/reload-stop-go@2x.png os=Darwin osversion>=10.10
% override chrome://browser/skin/sync-horizontalbar.png chrome://browser/skin/yosemite/sync-horizontalbar.png os=Darwin osversion>=10.10

Двоичные данные
browser/themes/osx/loop/menuPanel-yosemite.png

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

До

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

После

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

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

До

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

После

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

Двоичные данные
browser/themes/osx/loop/menuPanel.png

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

До

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

После

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

Двоичные данные
browser/themes/osx/loop/menuPanel@2x.png

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

До

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

После

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

Двоичные данные
browser/themes/osx/loop/toolbar-inverted.png

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

До

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

После

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

Двоичные данные
browser/themes/osx/loop/toolbar-inverted@2x.png

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

До

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

После

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

Двоичные данные
browser/themes/osx/loop/toolbar-yosemite.png

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

До

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

После

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

Двоичные данные
browser/themes/osx/loop/toolbar-yosemite@2x.png

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

До

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

После

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

Двоичные данные
browser/themes/osx/loop/toolbar.png

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

До

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

После

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

Двоичные данные
browser/themes/osx/loop/toolbar@2x.png

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

До

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

После

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

Двоичные данные
browser/themes/osx/preferences/checkbox-yosemite.png Normal file

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

После

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

Двоичные данные
browser/themes/osx/preferences/checkbox-yosemite@2x.png Normal file

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

После

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

Двоичные данные
browser/themes/osx/preferences/checkbox.png Normal file

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

После

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

Двоичные данные
browser/themes/osx/preferences/checkbox@2x.png Normal file

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

После

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

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

@ -0,0 +1,54 @@
/* 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/. */
#defaultEngine > .menulist-label-box > .menulist-icon {
height: 16px;
}
#engineList {
margin: .5em 6px;
}
#engineList treechildren::-moz-tree-image(engineShown) {
list-style-image: url("chrome://browser/skin/preferences/checkbox.png");
-moz-image-region: rect(0, 16px, 16px, 0);
-moz-margin-start: 4px;
-moz-margin-end: 1px;
}
#engineList treechildren::-moz-tree-image(engineShown, checked) {
-moz-image-region: rect(0, 48px, 16px, 32px);
}
@media (min-resolution: 2dppx) {
#engineList treechildren::-moz-tree-image(engineShown) {
list-style-image: url("chrome://browser/skin/preferences/checkbox@2x.png");
-moz-image-region: rect(0, 32px, 32px, 0);
width: 16px;
height: 16px;
}
#engineList treechildren::-moz-tree-image(engineShown, checked) {
-moz-image-region: rect(0, 96px, 32px, 64px);
}
}
#engineList treechildren::-moz-tree-image(engineName) {
-moz-margin-end: 4px;
-moz-margin-start: 1px;
width: 16px;
height: 16px;
}
#engineList treechildren::-moz-tree-row {
min-height: 20px;
}
#engineList treechildren::-moz-tree-drop-feedback {
background-color: Highlight;
width: 10000px; /* 100% doesn't work; 10k is hopefully larger than any window
we may have, overflow isn't visible. */
height: 2px;
-moz-margin-start: 0;
}

Двоичные данные
browser/themes/shared/incontentprefs/check.png Normal file

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

После

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

Двоичные данные
browser/themes/shared/incontentprefs/check@2x.png Normal file

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

После

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

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

@ -0,0 +1,54 @@
/* 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/. */
#defaultEngine > .menulist-label-box > .menulist-icon {
height: 16px;
}
/* work around a display: none in Linux's menu.css, see bug 1112310 */
.searchengine-menuitem > .menu-iconic-left {
display: -moz-box;
}
#engineList {
margin: .5em 6px;
}
#engineList treechildren::-moz-tree-image(engineShown, checked) {
/* Unfortunately check.svg can't be used in XUL trees. */
list-style-image: url("check.png");
-moz-margin-start: 5px;
width: 12px;
height: 12px;
}
@media (min-resolution: 2dppx) {
#engineList treechildren::-moz-tree-image(engineShown, checked) {
list-style-image: url("check@2x.png");
}
}
#engineList treechildren::-moz-tree-image(engineName) {
-moz-margin-end: 10px;
-moz-margin-start: 1px;
width: 16px;
height: 16px;
}
#engineList treechildren::-moz-tree-row {
min-height: 36px;
}
#engineList treechildren::-moz-tree-cell-text {
/* Override to avoid text in the selected row being white on light gray. */
color: -moz-FieldText;
}
#engineList treechildren::-moz-tree-drop-feedback {
background-color: Highlight;
width: 10000px; /* 100% doesn't work; 10k is hopefully larger than any window
we may have, overflow isn't visible. */
height: 2px;
-moz-margin-start: 0;
}

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

@ -118,9 +118,9 @@ browser.jar:
skin/classic/browser/webRTC-microphone-white-16.png (../shared/webrtc/microphone-white-16.png)
skin/classic/browser/webRTC-screen-white-16.png (../shared/webrtc/screen-white-16.png)
skin/classic/browser/loop/menuPanel.png (loop/menuPanel.png)
skin/classic/browser/loop/toolbar.png (loop/toolbar-XPVista7.png)
skin/classic/browser/loop/toolbar-lunaSilver.png (loop/toolbar-lunaSilver.png)
skin/classic/browser/loop/toolbar.png (loop/toolbar-XP.png)
skin/classic/browser/loop/toolbar-inverted.png (loop/toolbar-inverted.png)
skin/classic/browser/loop/toolbar-lunaSilver.png (loop/toolbar-lunaSilver.png)
skin/classic/browser/customizableui/background-noise-toolbar.png (customizableui/background-noise-toolbar.png)
skin/classic/browser/customizableui/customizeFavicon.ico (../shared/customizableui/customizeFavicon.ico)
skin/classic/browser/customizableui/customize-illustration.png (../shared/customizableui/customize-illustration.png)
@ -195,8 +195,14 @@ browser.jar:
skin/classic/browser/preferences/in-content/favicon.ico (../shared/incontentprefs/favicon.ico)
skin/classic/browser/preferences/in-content/icons.png (../shared/incontentprefs/icons.png)
skin/classic/browser/preferences/in-content/icons@2x.png (../shared/incontentprefs/icons@2x.png)
skin/classic/browser/preferences/in-content/search.css (../shared/incontentprefs/search.css)
skin/classic/browser/preferences/in-content/check.png (../shared/incontentprefs/check.png)
skin/classic/browser/preferences/in-content/check@2x.png (../shared/incontentprefs/check@2x.png)
skin/classic/browser/preferences/applications.css (preferences/applications.css)
skin/classic/browser/preferences/aboutPermissions.css (preferences/aboutPermissions.css)
skin/classic/browser/preferences/search.css (preferences/search.css)
skin/classic/browser/preferences/checkbox.png (preferences/checkbox-xp.png)
skin/classic/browser/preferences/checkbox-classic.png (preferences/checkbox-classic.png)
skin/classic/browser/social/services-16.png (social/services-16.png)
skin/classic/browser/social/services-64.png (social/services-64.png)
skin/classic/browser/social/chat-icons.svg (../shared/social/chat-icons.svg)
@ -564,9 +570,10 @@ browser.jar:
skin/classic/aero/browser/webRTC-camera-white-16.png (../shared/webrtc/camera-white-16.png)
skin/classic/aero/browser/webRTC-microphone-white-16.png (../shared/webrtc/microphone-white-16.png)
skin/classic/aero/browser/webRTC-screen-white-16.png (../shared/webrtc/screen-white-16.png)
skin/classic/aero/browser/loop/menuPanel.png (loop/menuPanel-aero.png)
skin/classic/aero/browser/loop/menuPanel.png (loop/menuPanel.png)
skin/classic/aero/browser/loop/menuPanel-aero.png (loop/menuPanel-aero.png)
skin/classic/aero/browser/loop/toolbar.png (loop/toolbar.png)
skin/classic/aero/browser/loop/toolbar-XPVista7.png (loop/toolbar-XPVista7.png)
skin/classic/aero/browser/loop/toolbar-aero.png (loop/toolbar-aero.png)
skin/classic/aero/browser/loop/toolbar-inverted.png (loop/toolbar-inverted.png)
skin/classic/aero/browser/customizableui/background-noise-toolbar.png (customizableui/background-noise-toolbar.png)
skin/classic/aero/browser/customizableui/customize-illustration.png (../shared/customizableui/customize-illustration.png)
@ -643,8 +650,15 @@ browser.jar:
skin/classic/aero/browser/preferences/in-content/favicon.ico (../shared/incontentprefs/favicon.ico)
skin/classic/aero/browser/preferences/in-content/icons.png (../shared/incontentprefs/icons.png)
skin/classic/aero/browser/preferences/in-content/icons@2x.png (../shared/incontentprefs/icons@2x.png)
skin/classic/aero/browser/preferences/in-content/search.css (../shared/incontentprefs/search.css)
skin/classic/aero/browser/preferences/in-content/check.png (../shared/incontentprefs/check.png)
skin/classic/aero/browser/preferences/in-content/check@2x.png (../shared/incontentprefs/check@2x.png)
skin/classic/aero/browser/preferences/applications.css (preferences/applications.css)
skin/classic/aero/browser/preferences/aboutPermissions.css (preferences/aboutPermissions.css)
skin/classic/aero/browser/preferences/search.css (preferences/search.css)
skin/classic/aero/browser/preferences/checkbox.png (preferences/checkbox-8.png)
skin/classic/aero/browser/preferences/checkbox-aero.png (preferences/checkbox-aero.png)
skin/classic/aero/browser/preferences/checkbox-classic.png (preferences/checkbox-classic.png)
skin/classic/aero/browser/social/services-16.png (social/services-16.png)
skin/classic/aero/browser/social/services-64.png (social/services-64.png)
skin/classic/aero/browser/social/chat-icons.svg (../shared/social/chat-icons.svg)
@ -902,6 +916,9 @@ browser.jar:
% override chrome://browser/skin/menuPanel-small.png chrome://browser/skin/menuPanel-small-aero.png os=WINNT osversion=6
% override chrome://browser/skin/menuPanel-small.png chrome://browser/skin/menuPanel-small-aero.png os=WINNT osversion=6.1
% override chrome://browser/skin/preferences/checkbox.png chrome://browser/skin/preferences/checkbox-aero.png os=WINNT osversion=6
% override chrome://browser/skin/preferences/checkbox.png chrome://browser/skin/preferences/checkbox-aero.png os=WINNT osversion=6.1
% override chrome://browser/skin/theme-switcher-icon.png chrome://browser/skin/theme-switcher-icon-aero.png os=WINNT osversion=6
% override chrome://browser/skin/theme-switcher-icon.png chrome://browser/skin/theme-switcher-icon-aero.png os=WINNT osversion=6.1
@ -917,5 +934,8 @@ browser.jar:
% override chrome://browser/skin/tabbrowser/tab-arrow-left.png chrome://browser/skin/tabbrowser/tab-arrow-left-XPVista7.png os=WINNT osversion=6
% override chrome://browser/skin/tabbrowser/tab-arrow-left.png chrome://browser/skin/tabbrowser/tab-arrow-left-XPVista7.png os=WINNT osversion=6.1
% override chrome://browser/skin/loop/toolbar.png chrome://browser/skin/loop/toolbar-XPVista7.png os=WINNT osversion=6
% override chrome://browser/skin/loop/toolbar.png chrome://browser/skin/loop/toolbar-XPVista7.png os=WINNT osversion=6.1
% override chrome://browser/skin/loop/toolbar.png chrome://browser/skin/loop/toolbar-aero.png os=WINNT osversion=6
% override chrome://browser/skin/loop/toolbar.png chrome://browser/skin/loop/toolbar-aero.png os=WINNT osversion=6.1
% override chrome://browser/skin/loop/menuPanel.png chrome://browser/skin/loop/menuPanel-aero.png os=WINNT osversion=6
% override chrome://browser/skin/loop/menuPanel.png chrome://browser/skin/loop/menuPanel-aero.png os=WINNT osversion=6.1

Двоичные данные
browser/themes/windows/loop/menuPanel-aero.png

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

До

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

После

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

Двоичные данные
browser/themes/windows/loop/menuPanel.png

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

До

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

После

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

Двоичные данные
browser/themes/windows/loop/toolbar-XP.png Normal file

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

После

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

Двоичные данные
browser/themes/windows/loop/toolbar-XPVista7.png

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

До

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

Двоичные данные
browser/themes/windows/loop/toolbar-aero.png Normal file

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

После

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

Двоичные данные
browser/themes/windows/loop/toolbar-inverted.png

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

До

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

После

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

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

До

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

После

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

Двоичные данные
browser/themes/windows/loop/toolbar.png

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

До

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

После

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

Двоичные данные
browser/themes/windows/preferences/checkbox-8.png Normal file

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

После

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

Двоичные данные
browser/themes/windows/preferences/checkbox-aero.png Normal file

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

После

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

Двоичные данные
browser/themes/windows/preferences/checkbox-classic.png Normal file

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

После

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

Двоичные данные
browser/themes/windows/preferences/checkbox-xp.png Normal file

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

После

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

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

@ -0,0 +1,59 @@
/* 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/. */
#defaultEngine > .menulist-label-box > .menulist-icon {
height: 16px;
}
#engineList {
margin: .5em 6px;
}
#engineList treechildren::-moz-tree-checkbox {
list-style-image: url("chrome://browser/skin/preferences/checkbox.png");
-moz-image-region: rect(0, 16px, 16px, 0);
}
#engineList treechildren::-moz-tree-checkbox(checked) {
-moz-image-region: rect(0, 64px, 16px, 48px);
}
@media (-moz-windows-classic) {
#engineList treechildren::-moz-tree-checkbox {
list-style-image: url("chrome://browser/skin/preferences/checkbox-classic.png");
}
#engineList treechildren::-moz-tree-checkbox(checked) {
-moz-image-region: rect(0, 48px, 16px, 32px);
}
}
@media not all and (-moz-windows-classic) {
#engineList treechildren::-moz-tree-checkbox(hover) {
-moz-image-region: rect(0, 48px, 16px, 32px);
}
#engineList treechildren::-moz-tree-checkbox(checked, hover) {
-moz-image-region: rect(0, 80px, 16px, 64px);
}
}
#engineList treechildren::-moz-tree-image(engineName) {
-moz-margin-end: 4px;
-moz-margin-start: 0;
width: 16px;
height: 16px;
}
#engineList treechildren::-moz-tree-row {
min-height: 20px;
}
#engineList treechildren::-moz-tree-drop-feedback {
background-color: Highlight;
width: 10000px; /* 100% doesn't work; 10k is hopefully larger than any window
we may have, overflow isn't visible. */
height: 2px;
-moz-margin-start: 0;
}

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

@ -3411,6 +3411,8 @@ TabChild::RecvRequestNotifyAfterRemotePaint()
bool
TabChild::RecvUIResolutionChanged()
{
mDPI = 0;
mDefaultScale = 0;
static_cast<PuppetWidget*>(mWidget.get())->ClearBackingScaleCache();
nsCOMPtr<nsIDocument> document(GetDocument());
nsCOMPtr<nsIPresShell> presShell = document->GetShell();

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

@ -219,7 +219,6 @@ MediaEngineTabVideoSource::Draw() {
IntSize size(mBufW, mBufH);
nsresult rv;
float scale = 1.0;
nsCOMPtr<nsPIDOMWindow> win = do_QueryInterface(mWindow);
@ -240,25 +239,14 @@ MediaEngineTabVideoSource::Draw() {
return;
}
float left, top, width, height;
rect->GetLeft(&left);
rect->GetTop(&top);
float width, height;
rect->GetWidth(&width);
rect->GetHeight(&height);
if (mScrollWithPage) {
nsPoint point;
utils->GetScrollXY(false, &point.x, &point.y);
left += point.x;
top += point.y;
}
if (width == 0 || height == 0) {
return;
}
int32_t srcX = left;
int32_t srcY = top;
int32_t srcW;
int32_t srcH;
@ -279,14 +267,15 @@ MediaEngineTabVideoSource::Draw() {
if (!presContext) {
return;
}
nscolor bgColor = NS_RGB(255, 255, 255);
nsCOMPtr<nsIPresShell> presShell = presContext->PresShell();
uint32_t renderDocFlags = (nsIPresShell::RENDER_IGNORE_VIEWPORT_SCROLLING |
nsIPresShell::RENDER_DOCUMENT_RELATIVE);
nsRect r(nsPresContext::CSSPixelsToAppUnits(srcX / scale),
nsPresContext::CSSPixelsToAppUnits(srcY / scale),
nsPresContext::CSSPixelsToAppUnits(srcW / scale),
nsPresContext::CSSPixelsToAppUnits(srcH / scale));
uint32_t renderDocFlags = nsIPresShell::RENDER_DOCUMENT_RELATIVE;
if (!mScrollWithPage) {
renderDocFlags |= nsIPresShell::RENDER_IGNORE_VIEWPORT_SCROLLING;
}
nsRect r(0, 0, nsPresContext::CSSPixelsToAppUnits((float)srcW),
nsPresContext::CSSPixelsToAppUnits((float)srcH));
gfxImageFormat format = gfxImageFormat::RGB24;
uint32_t stride = gfxASurface::FormatStrideForWidth(format, size.width);
@ -302,9 +291,9 @@ MediaEngineTabVideoSource::Draw() {
return;
}
nsRefPtr<gfxContext> context = new gfxContext(dt);
context->SetMatrix(
context->CurrentMatrix().Scale(scale * size.width / srcW,
scale * size.height / srcH));
context->SetMatrix(context->CurrentMatrix().Scale((float)size.width/srcW,
(float)size.height/srcH));
rv = presShell->RenderDocument(r, renderDocFlags, bgColor, context);
NS_ENSURE_SUCCESS_VOID(rv);

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

@ -280,13 +280,13 @@ pref("browser.search.noCurrentEngine", true);
pref("browser.search.official", true);
#endif
// Control media casting feature
// Control media casting & mirroring features
pref("browser.casting.enabled", true);
pref("browser.mirroring.enabled", true);
#ifdef RELEASE_BUILD
pref("browser.mirroring.enabled", false);
// Roku does not yet support mirroring in production
pref("browser.mirroring.enabled.roku", false);
#else
pref("browser.mirroring.enabled", true);
pref("browser.mirroring.enabled.roku", true);
#endif

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

@ -422,19 +422,38 @@ extern int
__real_getaddrinfo(const char *hostname, const char *servname,
const struct addrinfo *hints, struct addrinfo **res);
int android_sdk_version;
#pragma GCC visibility pop
int android_sdk_version = -1;
static int get_android_sdk_version()
{
char version_str[PROP_VALUE_MAX];
memset(version_str, 0, PROP_VALUE_MAX);
int len = __system_property_get("ro.build.version.sdk", version_str);
if (len < 1) {
#ifdef MOZ_GETADDRINFO_LOG_VERBOSE
__android_log_print(ANDROID_LOG_INFO, "getaddrinfo",
"Failed to get Android SDK version\n");
#endif
return len;
}
return (int)strtol(version_str, NULL, 10);
}
static int honeycomb_or_later()
{
static int android_sdk_version = 0;
if (android_sdk_version == 0) {
android_sdk_version = get_android_sdk_version();
}
#ifdef MOZ_GETADDRINFO_LOG_VERBOSE
__android_log_print(ANDROID_LOG_INFO, "getaddrinfo",
"I am%s Honeycomb\n",
(android_sdk_version >= 11) ? "" : " not");
#endif
return android_sdk_version >= 11;
}

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

@ -10,8 +10,6 @@ const {Services} = Cu.import("resource://gre/modules/Services.jsm", {});
const COLOR_UNIT_PREF = "devtools.defaultColorUnit";
const REGEX_JUST_QUOTES = /^""$/;
const REGEX_RGB_3_TUPLE = /^rgb\(([\d.]+),\s*([\d.]+),\s*([\d.]+)\)$/i;
const REGEX_RGBA_4_TUPLE = /^rgba\(([\d.]+),\s*([\d.]+),\s*([\d.]+),\s*([\d.]+|1|0)\)$/i;
const REGEX_HSL_3_TUPLE = /^\bhsl\(([\d.]+),\s*([\d.]+%),\s*([\d.]+%)\)$/i;
/**
@ -106,7 +104,7 @@ CssColor.prototype = {
},
get valid() {
return this._validateColor(this.authored);
return DOMUtils.isValidCSSColor(this.authored);
},
/**
@ -126,11 +124,9 @@ CssColor.prototype = {
},
get name() {
if (!this.valid) {
return "";
}
if (this.specialValue) {
return this.specialValue;
let invalidOrSpecialValue = this._getInvalidOrSpecialValue();
if (invalidOrSpecialValue !== false) {
return invalidOrSpecialValue;
}
try {
@ -147,11 +143,9 @@ CssColor.prototype = {
},
get hex() {
if (!this.valid) {
return "";
}
if (this.specialValue) {
return this.specialValue;
let invalidOrSpecialValue = this._getInvalidOrSpecialValue();
if (invalidOrSpecialValue !== false) {
return invalidOrSpecialValue;
}
if (this.hasAlpha) {
return this.rgba;
@ -167,11 +161,9 @@ CssColor.prototype = {
},
get longHex() {
if (!this.valid) {
return "";
}
if (this.specialValue) {
return this.specialValue;
let invalidOrSpecialValue = this._getInvalidOrSpecialValue();
if (invalidOrSpecialValue !== false) {
return invalidOrSpecialValue;
}
if (this.hasAlpha) {
return this.rgba;
@ -182,11 +174,9 @@ CssColor.prototype = {
},
get rgb() {
if (!this.valid) {
return "";
}
if (this.specialValue) {
return this.specialValue;
let invalidOrSpecialValue = this._getInvalidOrSpecialValue();
if (invalidOrSpecialValue !== false) {
return invalidOrSpecialValue;
}
if (!this.hasAlpha) {
if (this.authored.startsWith("rgb(")) {
@ -200,11 +190,9 @@ CssColor.prototype = {
},
get rgba() {
if (!this.valid) {
return "";
}
if (this.specialValue) {
return this.specialValue;
let invalidOrSpecialValue = this._getInvalidOrSpecialValue();
if (invalidOrSpecialValue !== false) {
return invalidOrSpecialValue;
}
if (this.authored.startsWith("rgba(")) {
// The color is valid and begins with rgba(. Return the authored value.
@ -218,11 +206,9 @@ CssColor.prototype = {
},
get hsl() {
if (!this.valid) {
return "";
}
if (this.specialValue) {
return this.specialValue;
let invalidOrSpecialValue = this._getInvalidOrSpecialValue();
if (invalidOrSpecialValue !== false) {
return invalidOrSpecialValue;
}
if (this.authored.startsWith("hsl(")) {
// The color is valid and begins with hsl(. Return the authored value.
@ -235,11 +221,9 @@ CssColor.prototype = {
},
get hsla() {
if (!this.valid) {
return "";
}
if (this.specialValue) {
return this.specialValue;
let invalidOrSpecialValue = this._getInvalidOrSpecialValue();
if (invalidOrSpecialValue !== false) {
return invalidOrSpecialValue;
}
if (this.authored.startsWith("hsla(")) {
// The color is valid and begins with hsla(. Return the authored value.
@ -252,6 +236,27 @@ CssColor.prototype = {
return this._hslNoAlpha().replace("hsl", "hsla").replace(")", ", 1)");
},
/**
* Check whether the current color value is in the special list e.g.
* transparent or invalid.
*
* @return {String|Boolean}
* - If the current color is a special value e.g. "transparent" then
* return the color.
* - If the color is invalid return an empty string.
* - If the color is a regular color e.g. #F06 so we return false
* to indicate that the color is neither invalid or special.
*/
_getInvalidOrSpecialValue: function() {
if (this.specialValue) {
return this.specialValue;
}
if (!this.valid) {
return "";
}
return false;
},
/**
* Change color
*
@ -298,27 +303,11 @@ CssColor.prototype = {
* appropriate.
*/
_getRGBATuple: function() {
let win = Services.appShell.hiddenDOMWindow;
let doc = win.document;
let span = doc.createElement("span");
span.style.color = this.authored;
let computed = win.getComputedStyle(span).color;
let tuple = DOMUtils.colorToRGBA(this.authored);
if (computed === "transparent") {
return {r: 0, g: 0, b: 0, a: 0};
}
tuple.a = parseFloat(tuple.a.toFixed(1));
let rgba = computed.match(REGEX_RGBA_4_TUPLE);
if (rgba) {
let [, r, g, b, a] = rgba;
return {r: r, g: g, b: b, a: a};
} else {
let rgb = computed.match(REGEX_RGB_3_TUPLE);
let [, r, g, b] = rgb;
return {r: r, g: g, b: b, a: 1};
}
return tuple;
},
_hslNoAlpha: function() {
@ -342,33 +331,6 @@ CssColor.prototype = {
valueOf: function() {
return this.rgba;
},
_validateColor: function(color) {
if (typeof color !== "string" || color === "") {
return false;
}
let win = Services.appShell.hiddenDOMWindow;
let doc = win.document;
// Create a black span in a hidden window.
let span = doc.createElement("span");
span.style.color = "rgb(0, 0, 0)";
// Attempt to set the color. If the color is no longer black we know that
// color is valid.
span.style.color = color;
if (span.style.color !== "rgb(0, 0, 0)") {
return true;
}
// If the color is black then the above check will have failed. We change
// the span to white and attempt to reapply the color. If the span is not
// white then we know that the color is valid otherwise we return invalid.
span.style.color = "rgb(255, 255, 255)";
span.style.color = color;
return span.style.color !== "rgb(255, 255, 255)";
},
};
/**

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

@ -337,15 +337,7 @@ OutputParser.prototype = {
* CSS Property value to check
*/
_cssPropertySupportsValue: function(name, value) {
let win = Services.appShell.hiddenDOMWindow;
let doc = win.document;
value = value.replace("!important", "");
let div = doc.createElement("div");
div.style.setProperty(name, value);
return !!div.style.getPropertyValue(name);
return DOMUtils.cssPropertyIsValid(name, value);
},
/**

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

@ -285,23 +285,24 @@ function RemoteMirror(url, win, viewport, mirrorStartedCallback, contentWindow)
this._iceCandidates = [];
this.mirrorStarted = mirrorStartedCallback;
// This code insures the generated tab mirror is not wider than 800 nor taller than 600
// This code insures the generated tab mirror is not wider than 1280 nor taller than 720
// Better dimensions should be chosen after the Roku Channel is working.
let windowId = contentWindow.QueryInterface(Ci.nsIInterfaceRequestor).getInterface(Ci.nsIDOMWindowUtils).outerWindowID;
let cWidth = Math.max(viewport.cssWidth, viewport.width);
let cHeight = Math.max(viewport.cssHeight, viewport.height);
const MAX_WIDTH = 800;
const MAX_HEIGHT = 600;
const MAX_WIDTH = 1280;
const MAX_HEIGHT = 720;
let tWidth = 0;
let tHeight = 0;
// division and multiplication by 4 to ensure dimensions are multiples of 4
if ((cWidth / MAX_WIDTH) > (cHeight / MAX_HEIGHT)) {
tHeight = Math.ceil((MAX_WIDTH / cWidth) * cHeight);
tHeight = Math.ceil((MAX_WIDTH / (4* cWidth)) * cHeight) * 4;
tWidth = MAX_WIDTH;
} else {
tWidth = Math.ceil((MAX_HEIGHT / cHeight) * cWidth);
tWidth = Math.ceil((MAX_HEIGHT / (4 * cHeight)) * cWidth) * 4;
tHeight = MAX_HEIGHT;
}