Merge autoland to m-c, a=merge
MozReview-Commit-ID: HAS6ji3eShV
|
@ -57,6 +57,11 @@ var gAdvancedPane = {
|
|||
|
||||
if (Services.prefs.getBoolPref("browser.storageManager.enabled")) {
|
||||
Services.obs.addObserver(this, "sitedatamanager:sites-updated", false);
|
||||
let unload = () => {
|
||||
window.removeEventListener("unload", unload);
|
||||
Services.obs.removeObserver(this, "sitedatamanager:sites-updated");
|
||||
};
|
||||
window.addEventListener("unload", unload);
|
||||
SiteDataManager.updateSites();
|
||||
setEventListener("clearSiteDataButton", "command",
|
||||
gAdvancedPane.clearSiteData);
|
||||
|
|
|
@ -8,8 +8,10 @@
|
|||
|
||||
"use strict";
|
||||
|
||||
const {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components;
|
||||
const {classes: Cc, interfaces: Ci, utils: Cu, results: Cr, manager: Cm} = Components;
|
||||
|
||||
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||
let {FormAutoCompleteResult} = Cu.import("resource://gre/modules/nsFormAutoCompleteResult.jsm", {});
|
||||
|
||||
/**
|
||||
* Handles profile autofill for a DOM Form element.
|
||||
|
@ -131,3 +133,110 @@ FormAutofillHandler.prototype = {
|
|||
}
|
||||
},
|
||||
};
|
||||
|
||||
// Register/unregister a constructor as a factory.
|
||||
function AutocompleteFactory() {}
|
||||
AutocompleteFactory.prototype = {
|
||||
register(targetConstructor) {
|
||||
let proto = targetConstructor.prototype;
|
||||
this._classID = proto.classID;
|
||||
|
||||
let factory = XPCOMUtils._getFactory(targetConstructor);
|
||||
this._factory = factory;
|
||||
|
||||
let registrar = Cm.QueryInterface(Ci.nsIComponentRegistrar);
|
||||
registrar.registerFactory(proto.classID, proto.classDescription,
|
||||
proto.contractID, factory);
|
||||
|
||||
if (proto.classID2) {
|
||||
this._classID2 = proto.classID2;
|
||||
registrar.registerFactory(proto.classID2, proto.classDescription,
|
||||
proto.contractID2, factory);
|
||||
}
|
||||
},
|
||||
|
||||
unregister() {
|
||||
let registrar = Cm.QueryInterface(Ci.nsIComponentRegistrar);
|
||||
registrar.unregisterFactory(this._classID, this._factory);
|
||||
if (this._classID2) {
|
||||
registrar.unregisterFactory(this._classID2, this._factory);
|
||||
}
|
||||
this._factory = null;
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @constructor
|
||||
*
|
||||
* @implements {nsIAutoCompleteSearch}
|
||||
*/
|
||||
function AutofillProfileAutoCompleteSearch() {
|
||||
|
||||
}
|
||||
AutofillProfileAutoCompleteSearch.prototype = {
|
||||
classID: Components.ID("4f9f1e4c-7f2c-439e-9c9e-566b68bc187d"),
|
||||
contractID: "@mozilla.org/autocomplete/search;1?name=autofill-profiles",
|
||||
classDescription: "AutofillProfileAutoCompleteSearch",
|
||||
QueryInterface: XPCOMUtils.generateQI([Ci.nsIAutoCompleteSearch]),
|
||||
|
||||
// Begin nsIAutoCompleteSearch implementation
|
||||
|
||||
/**
|
||||
* Searches for a given string and notifies a listener (either synchronously
|
||||
* or asynchronously) of the result
|
||||
*
|
||||
* @param {string} searchString the string to search for
|
||||
* @param {string} searchParam
|
||||
* @param {Object} previousResult a previous result to use for faster searchinig
|
||||
* @param {Object} listener the listener to notify when the search is complete
|
||||
*/
|
||||
startSearch(searchString, searchParam, previousResult, listener) {
|
||||
// TODO: These mock data should be replaced by form autofill API
|
||||
let labels = ["Mary", "John"];
|
||||
let values = ["Mary S.", "John S."];
|
||||
let comments = ["123 Sesame Street.", "331 E. Evelyn Avenue"];
|
||||
let result = new FormAutoCompleteResult(searchString,
|
||||
Ci.nsIAutoCompleteResult.RESULT_SUCCESS,
|
||||
0, "", values, labels,
|
||||
comments);
|
||||
|
||||
listener.onSearchResult(this, result);
|
||||
},
|
||||
|
||||
/**
|
||||
* Stops an asynchronous search that is in progress
|
||||
*/
|
||||
stopSearch() {
|
||||
},
|
||||
};
|
||||
|
||||
this.NSGetFactory = XPCOMUtils.generateNSGetFactory([AutofillProfileAutoCompleteSearch]);
|
||||
|
||||
// TODO: Remove this lint option once we apply ProfileAutocomplete while
|
||||
// content script initialization.
|
||||
/* eslint no-unused-vars: [2, {"vars": "local"}] */
|
||||
let ProfileAutocomplete = {
|
||||
_registered: false,
|
||||
_factory: null,
|
||||
|
||||
ensureRegistered() {
|
||||
if (this._registered) {
|
||||
return;
|
||||
}
|
||||
|
||||
this._factory = new AutocompleteFactory();
|
||||
this._factory.register(AutofillProfileAutoCompleteSearch);
|
||||
this._registered = true;
|
||||
},
|
||||
|
||||
ensureUnregistered() {
|
||||
if (!this._registered) {
|
||||
return;
|
||||
}
|
||||
|
||||
this._factory.unregister();
|
||||
this._factory = null;
|
||||
this._registered = false;
|
||||
},
|
||||
};
|
||||
|
|
|
@ -48,6 +48,7 @@
|
|||
|
||||
#PopupAutoComplete > richlistbox > richlistitem[originaltype="insecureWarning"][selected] {
|
||||
background-color: var(--arrowpanel-dimmed-further);
|
||||
color: -moz-DialogText;
|
||||
}
|
||||
|
||||
#PopupAutoComplete > richlistbox > richlistitem[originaltype="insecureWarning"] > .ac-title {
|
||||
|
|
|
@ -5,9 +5,9 @@
|
|||
"use strict";
|
||||
|
||||
const {
|
||||
TOGGLE_FILTER_TYPE,
|
||||
ENABLE_FILTER_TYPE_ONLY,
|
||||
SET_FILTER_TEXT,
|
||||
TOGGLE_REQUEST_FILTER_TYPE,
|
||||
ENABLE_REQUEST_FILTER_TYPE_ONLY,
|
||||
SET_REQUEST_FILTER_TEXT,
|
||||
} = require("../constants");
|
||||
|
||||
/**
|
||||
|
@ -17,9 +17,9 @@ const {
|
|||
*
|
||||
* @param {string} filter - A filter type is going to be updated
|
||||
*/
|
||||
function toggleFilterType(filter) {
|
||||
function toggleRequestFilterType(filter) {
|
||||
return {
|
||||
type: TOGGLE_FILTER_TYPE,
|
||||
type: TOGGLE_REQUEST_FILTER_TYPE,
|
||||
filter,
|
||||
};
|
||||
}
|
||||
|
@ -32,9 +32,9 @@ function toggleFilterType(filter) {
|
|||
*
|
||||
* @param {string} filter - A filter type is going to be updated
|
||||
*/
|
||||
function enableFilterTypeOnly(filter) {
|
||||
function enableRequestFilterTypeOnly(filter) {
|
||||
return {
|
||||
type: ENABLE_FILTER_TYPE_ONLY,
|
||||
type: ENABLE_REQUEST_FILTER_TYPE_ONLY,
|
||||
filter,
|
||||
};
|
||||
}
|
||||
|
@ -44,15 +44,15 @@ function enableFilterTypeOnly(filter) {
|
|||
*
|
||||
* @param {string} text - A filter text is going to be set
|
||||
*/
|
||||
function setFilterText(text) {
|
||||
function setRequestFilterText(text) {
|
||||
return {
|
||||
type: SET_FILTER_TEXT,
|
||||
type: SET_REQUEST_FILTER_TEXT,
|
||||
text,
|
||||
};
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
toggleFilterType,
|
||||
enableFilterTypeOnly,
|
||||
setFilterText,
|
||||
toggleRequestFilterType,
|
||||
enableRequestFilterTypeOnly,
|
||||
setRequestFilterText,
|
||||
};
|
||||
|
|
|
@ -12,10 +12,10 @@ const Actions = require("../actions/index");
|
|||
const { button, div } = DOM;
|
||||
|
||||
function FilterButtons({
|
||||
filterTypes,
|
||||
triggerFilterType,
|
||||
requestFilterTypes,
|
||||
toggleRequestFilterType,
|
||||
}) {
|
||||
const buttons = filterTypes.entrySeq().map(([type, checked]) => {
|
||||
const buttons = requestFilterTypes.entrySeq().map(([type, checked]) => {
|
||||
let classList = ["menu-filter-button"];
|
||||
checked && classList.push("checked");
|
||||
|
||||
|
@ -23,8 +23,8 @@ function FilterButtons({
|
|||
id: `requests-menu-filter-${type}-button`,
|
||||
className: classList.join(" "),
|
||||
"data-key": type,
|
||||
onClick: triggerFilterType,
|
||||
onKeyDown: triggerFilterType,
|
||||
onClick: toggleRequestFilterType,
|
||||
onKeyDown: toggleRequestFilterType,
|
||||
}, L10N.getStr(`netmonitor.toolbar.filter.${type}`));
|
||||
}).toArray();
|
||||
|
||||
|
@ -33,17 +33,17 @@ function FilterButtons({
|
|||
|
||||
FilterButtons.propTypes = {
|
||||
state: PropTypes.object.isRequired,
|
||||
triggerFilterType: PropTypes.func.iRequired,
|
||||
toggleRequestFilterType: PropTypes.func.iRequired,
|
||||
};
|
||||
|
||||
module.exports = connect(
|
||||
(state) => ({ filterTypes: state.filters.types }),
|
||||
(state) => ({ requestFilterTypes: state.filters.requestFilterTypes }),
|
||||
(dispatch) => ({
|
||||
triggerFilterType: (evt) => {
|
||||
toggleRequestFilterType: (evt) => {
|
||||
if (evt.type === "keydown" && (evt.key !== "" || evt.key !== "Enter")) {
|
||||
return;
|
||||
}
|
||||
dispatch(Actions.toggleFilterType(evt.target.dataset.key));
|
||||
dispatch(Actions.toggleRequestFilterType(evt.target.dataset.key));
|
||||
},
|
||||
})
|
||||
)(FilterButtons);
|
||||
|
|
|
@ -19,7 +19,7 @@ module.exports = connect(
|
|||
}),
|
||||
(dispatch) => ({
|
||||
onChange: (url) => {
|
||||
dispatch(Actions.setFilterText(url));
|
||||
dispatch(Actions.setRequestFilterText(url));
|
||||
},
|
||||
})
|
||||
)(SearchBox);
|
||||
|
|
|
@ -18,15 +18,15 @@ const actionTypes = {
|
|||
CLEAR_REQUESTS: "CLEAR_REQUESTS",
|
||||
CLEAR_TIMING_MARKERS: "CLEAR_TIMING_MARKERS",
|
||||
CLONE_SELECTED_REQUEST: "CLONE_SELECTED_REQUEST",
|
||||
ENABLE_FILTER_TYPE_ONLY: "ENABLE_FILTER_TYPE_ONLY",
|
||||
ENABLE_REQUEST_FILTER_TYPE_ONLY: "ENABLE_REQUEST_FILTER_TYPE_ONLY",
|
||||
OPEN_SIDEBAR: "OPEN_SIDEBAR",
|
||||
OPEN_STATISTICS: "OPEN_STATISTICS",
|
||||
PRESELECT_REQUEST: "PRESELECT_REQUEST",
|
||||
REMOVE_SELECTED_CUSTOM_REQUEST: "REMOVE_SELECTED_CUSTOM_REQUEST",
|
||||
SELECT_REQUEST: "SELECT_REQUEST",
|
||||
SET_FILTER_TEXT: "SET_FILTER_TEXT",
|
||||
SET_REQUEST_FILTER_TEXT: "SET_REQUEST_FILTER_TEXT",
|
||||
SORT_BY: "SORT_BY",
|
||||
TOGGLE_FILTER_TYPE: "TOGGLE_FILTER_TYPE",
|
||||
TOGGLE_REQUEST_FILTER_TYPE: "TOGGLE_REQUEST_FILTER_TYPE",
|
||||
UPDATE_REQUEST: "UPDATE_REQUEST",
|
||||
WATERFALL_RESIZE: "WATERFALL_RESIZE",
|
||||
};
|
||||
|
|
|
@ -266,7 +266,7 @@ var NetMonitorController = {
|
|||
request = getDisplayedRequestById(gStore.getState(), requestId);
|
||||
if (!request) {
|
||||
// Reset filters so that the request is visible.
|
||||
gStore.dispatch(Actions.toggleFilterType("all"));
|
||||
gStore.dispatch(Actions.toggleRequestFilterType("all"));
|
||||
request = getDisplayedRequestById(gStore.getState(), requestId);
|
||||
}
|
||||
|
||||
|
|
|
@ -6,9 +6,9 @@
|
|||
|
||||
const I = require("devtools/client/shared/vendor/immutable");
|
||||
const {
|
||||
TOGGLE_FILTER_TYPE,
|
||||
ENABLE_FILTER_TYPE_ONLY,
|
||||
SET_FILTER_TEXT,
|
||||
TOGGLE_REQUEST_FILTER_TYPE,
|
||||
ENABLE_REQUEST_FILTER_TYPE_ONLY,
|
||||
SET_REQUEST_FILTER_TEXT,
|
||||
} = require("../constants");
|
||||
|
||||
const FilterTypes = I.Record({
|
||||
|
@ -26,11 +26,11 @@ const FilterTypes = I.Record({
|
|||
});
|
||||
|
||||
const Filters = I.Record({
|
||||
types: new FilterTypes({ all: true }),
|
||||
text: "",
|
||||
requestFilterTypes: new FilterTypes({ all: true }),
|
||||
requestFilterText: "",
|
||||
});
|
||||
|
||||
function toggleFilterType(state, action) {
|
||||
function toggleRequestFilterType(state, action) {
|
||||
let { filter } = action;
|
||||
let newState;
|
||||
|
||||
|
@ -54,7 +54,7 @@ function toggleFilterType(state, action) {
|
|||
return newState;
|
||||
}
|
||||
|
||||
function enableFilterTypeOnly(state, action) {
|
||||
function enableRequestFilterTypeOnly(state, action) {
|
||||
let { filter } = action;
|
||||
|
||||
// Ignore unknown filter type
|
||||
|
@ -67,12 +67,14 @@ function enableFilterTypeOnly(state, action) {
|
|||
|
||||
function filters(state = new Filters(), action) {
|
||||
switch (action.type) {
|
||||
case TOGGLE_FILTER_TYPE:
|
||||
return state.set("types", toggleFilterType(state.types, action));
|
||||
case ENABLE_FILTER_TYPE_ONLY:
|
||||
return state.set("types", enableFilterTypeOnly(state.types, action));
|
||||
case SET_FILTER_TEXT:
|
||||
return state.set("text", action.text);
|
||||
case TOGGLE_REQUEST_FILTER_TYPE:
|
||||
return state.set("requestFilterTypes",
|
||||
toggleRequestFilterType(state.requestFilterTypes, action));
|
||||
case ENABLE_REQUEST_FILTER_TYPE_ONLY:
|
||||
return state.set("requestFilterTypes",
|
||||
enableRequestFilterTypeOnly(state.requestFilterTypes, action));
|
||||
case SET_REQUEST_FILTER_TEXT:
|
||||
return state.set("requestFilterText", action.text);
|
||||
default:
|
||||
return state;
|
||||
}
|
||||
|
|
|
@ -72,7 +72,7 @@ RequestsMenuView.prototype = {
|
|||
this.contextMenu = new RequestListContextMenu();
|
||||
this.contextMenu.initialize(store);
|
||||
|
||||
Prefs.filters.forEach(type => store.dispatch(Actions.toggleFilterType(type)));
|
||||
Prefs.filters.forEach(type => store.dispatch(Actions.toggleRequestFilterType(type)));
|
||||
|
||||
// Watch selection changes
|
||||
this.store.subscribe(storeWatcher(
|
||||
|
|
|
@ -5,7 +5,8 @@
|
|||
"use strict";
|
||||
|
||||
function getActiveFilters(state) {
|
||||
return state.filters.types.toSeq().filter(checked => checked).keySeq().toArray();
|
||||
return state.filters.requestFilterTypes.toSeq()
|
||||
.filter(checked => checked).keySeq().toArray();
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
|
|
|
@ -24,10 +24,10 @@ function getOrigRequest(requests, req) {
|
|||
const getFilterFn = createSelector(
|
||||
state => state.filters,
|
||||
filters => r => {
|
||||
const matchesType = filters.types.some((enabled, filter) => {
|
||||
const matchesType = filters.requestFilterTypes.some((enabled, filter) => {
|
||||
return enabled && Filters[filter] && Filters[filter](r);
|
||||
});
|
||||
return matchesType && isFreetextMatch(r, filters.text);
|
||||
return matchesType && isFreetextMatch(r, filters.requestFilterText);
|
||||
}
|
||||
);
|
||||
|
||||
|
|
|
@ -171,7 +171,7 @@ StatisticsView.prototype = {
|
|||
|
||||
chart.on("click", (_, item) => {
|
||||
// Reset FilterButtons and enable one filter exclusively
|
||||
this.store.dispatch(Actions.enableFilterTypeOnly(item.label));
|
||||
this.store.dispatch(Actions.enableRequestFilterTypeOnly(item.label));
|
||||
this.store.dispatch(Actions.openStatistics(false));
|
||||
});
|
||||
|
||||
|
|
|
@ -135,7 +135,7 @@ add_task(function* () {
|
|||
let { gStore } = monitor.panelWin;
|
||||
|
||||
function setFreetextFilter(value) {
|
||||
gStore.dispatch(Actions.setFilterText(value));
|
||||
gStore.dispatch(Actions.setRequestFilterText(value));
|
||||
}
|
||||
|
||||
info("Starting test... ");
|
||||
|
|
|
@ -28,7 +28,7 @@ add_task(function* () {
|
|||
info("Checking the image thumbnail when all items are sorted.");
|
||||
checkImageThumbnail();
|
||||
|
||||
gStore.dispatch(Actions.toggleFilterType("images"));
|
||||
gStore.dispatch(Actions.toggleRequestFilterType("images"));
|
||||
info("Checking the image thumbnail when only images are shown.");
|
||||
checkImageThumbnail();
|
||||
|
||||
|
|
|
@ -34,7 +34,7 @@ add_task(function* () {
|
|||
// Predicate used to modify the frontend when setting the new pref value,
|
||||
// before trying to validate the changes.
|
||||
modifyFrontend: ($, value) => value.forEach(e =>
|
||||
getStore().dispatch(Actions.toggleFilterType(e)))
|
||||
getStore().dispatch(Actions.toggleRequestFilterType(e)))
|
||||
},
|
||||
networkDetailsWidth: {
|
||||
newValue: ~~(Math.random() * 200 + 100),
|
||||
|
|
|
@ -57,7 +57,7 @@ describe("FilterButtons::enableFilterOnly:", () => {
|
|||
FilterButtons()
|
||||
));
|
||||
|
||||
store.dispatch(Actions.enableFilterTypeOnly("xhr"));
|
||||
store.dispatch(Actions.enableRequestFilterTypeOnly("xhr"));
|
||||
asExpected(wrapper, expectXHRTypes, `when enableFilterOnly("xhr") is called`);
|
||||
});
|
||||
|
||||
|
@ -83,8 +83,8 @@ describe("FilterButtons::toggleFilter:", () => {
|
|||
FilterButtons()
|
||||
));
|
||||
|
||||
store.dispatch(Actions.toggleFilterType("xhr"));
|
||||
store.dispatch(Actions.toggleFilterType("js"));
|
||||
store.dispatch(Actions.toggleRequestFilterType("xhr"));
|
||||
store.dispatch(Actions.toggleRequestFilterType("js"));
|
||||
asExpected(wrapper, expectXHRJSTypes, `when xhr, js is toggled`);
|
||||
});
|
||||
|
||||
|
|
|
@ -47,7 +47,7 @@ add_task(function* () {
|
|||
"The correct request is definitely selected");
|
||||
|
||||
// Filter out the HTML request.
|
||||
panel.panelWin.gStore.dispatch(Actions.toggleFilterType("js"));
|
||||
panel.panelWin.gStore.dispatch(Actions.toggleRequestFilterType("js"));
|
||||
|
||||
yield toolbox.selectTool("webconsole");
|
||||
is(toolbox.currentToolId, "webconsole", "Web console was selected");
|
||||
|
|
|
@ -441,6 +441,25 @@ var StyleSheetActor = protocol.ActorClassWithSpec(styleSheetSpec, {
|
|||
return promise.resolve(content);
|
||||
}
|
||||
|
||||
return this.fetchStylesheet(this.href).then(({ content }) => {
|
||||
this.text = content;
|
||||
return content;
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Fetch a stylesheet at the provided URL. Returns a promise that will resolve the
|
||||
* result of the fetch command.
|
||||
*
|
||||
* @param {String} href
|
||||
* The href of the stylesheet to retrieve.
|
||||
* @return {Promise} a promise that resolves with an object with the following members
|
||||
* on success:
|
||||
* - content: the document at that URL, as a string,
|
||||
* - contentType: the content type of the document
|
||||
* If an error occurs, the promise is rejected with that error.
|
||||
*/
|
||||
fetchStylesheet: Task.async(function* (href) {
|
||||
let options = {
|
||||
loadFromCache: true,
|
||||
policy: Ci.nsIContentPolicy.TYPE_INTERNAL_STYLESHEET,
|
||||
|
@ -451,19 +470,30 @@ var StyleSheetActor = protocol.ActorClassWithSpec(styleSheetSpec, {
|
|||
// stylesheets instead of the content principal since such stylesheets
|
||||
// require system principal to load. At meanwhile, we strip the loadGroup
|
||||
// for preventing the assertion of the userContextId mismatching.
|
||||
// The default internal stylesheets load from the 'resource:' URL.
|
||||
// Bug 1287607, 1291321 - 'chrome' and 'file' protocols should also be handled in the
|
||||
// same way.
|
||||
if (!/^(chrome|file|resource):\/\//.test(this.href)) {
|
||||
|
||||
// chrome|file|resource|moz-extension protocols rely on the system principal.
|
||||
let excludedProtocolsRe = /^(chrome|file|resource|moz-extension):\/\//;
|
||||
if (!excludedProtocolsRe.test(this.href)) {
|
||||
// Stylesheets using other protocols should use the content principal.
|
||||
options.window = this.window;
|
||||
options.principal = this.document.nodePrincipal;
|
||||
}
|
||||
|
||||
return fetch(this.href, options).then(({ content }) => {
|
||||
this.text = content;
|
||||
return content;
|
||||
});
|
||||
},
|
||||
let result;
|
||||
try {
|
||||
result = yield fetch(this.href, options);
|
||||
} catch (e) {
|
||||
// The list of excluded protocols can be missing some protocols, try to use the
|
||||
// system principal if the first fetch failed.
|
||||
console.error(`stylesheets actor: fetch failed for ${this.href},` +
|
||||
` using system principal instead.`);
|
||||
options.window = undefined;
|
||||
options.principal = undefined;
|
||||
result = yield fetch(this.href, options);
|
||||
}
|
||||
|
||||
return result;
|
||||
}),
|
||||
|
||||
/**
|
||||
* Protocol method to get the original source (actors) for this
|
||||
|
|
|
@ -2857,6 +2857,7 @@ exports.CSS_PROPERTIES = {
|
|||
"box-shadow",
|
||||
"box-sizing",
|
||||
"caption-side",
|
||||
"caret-color",
|
||||
"clear",
|
||||
"clip",
|
||||
"clip-path",
|
||||
|
@ -5261,6 +5262,28 @@ exports.CSS_PROPERTIES = {
|
|||
"unset"
|
||||
]
|
||||
},
|
||||
"caret-color": {
|
||||
"isInherited": true,
|
||||
"subproperties": [
|
||||
"caret-color"
|
||||
],
|
||||
"supports": [
|
||||
2
|
||||
],
|
||||
"values": [
|
||||
"COLOR",
|
||||
"auto",
|
||||
"currentColor",
|
||||
"hsl",
|
||||
"hsla",
|
||||
"inherit",
|
||||
"initial",
|
||||
"rgb",
|
||||
"rgba",
|
||||
"transparent",
|
||||
"unset"
|
||||
]
|
||||
},
|
||||
"clear": {
|
||||
"isInherited": false,
|
||||
"subproperties": [
|
||||
|
|
|
@ -486,13 +486,13 @@ public:
|
|||
|
||||
void HandleAudioDecoded(MediaData* aAudio) override
|
||||
{
|
||||
mMaster->Push(aAudio);
|
||||
mMaster->PushAudio(aAudio);
|
||||
MaybeFinishDecodeFirstFrame();
|
||||
}
|
||||
|
||||
void HandleVideoDecoded(MediaData* aVideo, TimeStamp aDecodeStart) override
|
||||
{
|
||||
mMaster->Push(aVideo);
|
||||
mMaster->PushVideo(aVideo);
|
||||
MaybeFinishDecodeFirstFrame();
|
||||
}
|
||||
|
||||
|
@ -579,13 +579,15 @@ public:
|
|||
|
||||
void HandleAudioDecoded(MediaData* aAudio) override
|
||||
{
|
||||
mMaster->Push(aAudio);
|
||||
mMaster->PushAudio(aAudio);
|
||||
mMaster->DispatchDecodeTasksIfNeeded();
|
||||
MaybeStopPrerolling();
|
||||
}
|
||||
|
||||
void HandleVideoDecoded(MediaData* aVideo, TimeStamp aDecodeStart) override
|
||||
{
|
||||
mMaster->Push(aVideo);
|
||||
mMaster->PushVideo(aVideo);
|
||||
mMaster->DispatchDecodeTasksIfNeeded();
|
||||
MaybeStopPrerolling();
|
||||
CheckSlowDecoding(aDecodeStart);
|
||||
}
|
||||
|
@ -867,7 +869,7 @@ public:
|
|||
// requests when AccurateSeekTask::Seek() begins. We will just store the data
|
||||
// without checking |mDiscontinuity| or calling DropAudioUpToSeekTarget().
|
||||
if (mSeekJob.mTarget->IsVideoOnly()) {
|
||||
mMaster->Push(aAudio);
|
||||
mMaster->PushAudio(aAudio);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -875,7 +877,7 @@ public:
|
|||
|
||||
if (mSeekJob.mTarget->IsFast()) {
|
||||
// Non-precise seek; we can stop the seek at the first sample.
|
||||
mMaster->Push(aAudio);
|
||||
mMaster->PushAudio(aAudio);
|
||||
mDoneAudioSeeking = true;
|
||||
} else {
|
||||
nsresult rv = DropAudioUpToSeekTarget(aAudio->As<AudioData>());
|
||||
|
@ -901,7 +903,7 @@ public:
|
|||
|
||||
if (mSeekJob.mTarget->IsFast()) {
|
||||
// Non-precise seek. We can stop the seek at the first sample.
|
||||
mMaster->Push(aVideo);
|
||||
mMaster->PushVideo(aVideo);
|
||||
mDoneVideoSeeking = true;
|
||||
} else {
|
||||
nsresult rv = DropVideoUpToSeekTarget(aVideo);
|
||||
|
@ -948,13 +950,13 @@ public:
|
|||
AudioQueue().Finish();
|
||||
mDoneAudioSeeking = true;
|
||||
} else {
|
||||
VideoQueue().Finish();
|
||||
mDoneVideoSeeking = true;
|
||||
if (mFirstVideoFrameAfterSeek) {
|
||||
// Hit the end of stream. Move mFirstVideoFrameAfterSeek into
|
||||
// mSeekedVideoData so we have something to display after seeking.
|
||||
mMaster->Push(mFirstVideoFrameAfterSeek);
|
||||
mMaster->PushVideo(mFirstVideoFrameAfterSeek);
|
||||
}
|
||||
VideoQueue().Finish();
|
||||
mDoneVideoSeeking = true;
|
||||
}
|
||||
MaybeFinishSeek();
|
||||
return;
|
||||
|
@ -1149,7 +1151,7 @@ private:
|
|||
// silence to cover the gap. Typically this happens in poorly muxed
|
||||
// files.
|
||||
SWARN("Audio not synced after seek, maybe a poorly muxed file?");
|
||||
mMaster->Push(aAudio);
|
||||
mMaster->PushAudio(aAudio);
|
||||
mDoneAudioSeeking = true;
|
||||
return NS_OK;
|
||||
}
|
||||
|
@ -1195,7 +1197,7 @@ private:
|
|||
channels,
|
||||
aAudio->mRate));
|
||||
MOZ_ASSERT(AudioQueue().GetSize() == 0, "Should be the 1st sample after seeking");
|
||||
mMaster->Push(data);
|
||||
mMaster->PushAudio(data);
|
||||
mDoneAudioSeeking = true;
|
||||
|
||||
return NS_OK;
|
||||
|
@ -1227,7 +1229,7 @@ private:
|
|||
video->mTime, video->GetEndTime(), target);
|
||||
|
||||
MOZ_ASSERT(VideoQueue().GetSize() == 0, "Should be the 1st sample after seeking");
|
||||
mMaster->Push(video);
|
||||
mMaster->PushVideo(video);
|
||||
mDoneVideoSeeking = true;
|
||||
}
|
||||
|
||||
|
@ -1361,7 +1363,7 @@ private:
|
|||
|
||||
void HandleAudioDecoded(MediaData* aAudio) override
|
||||
{
|
||||
mMaster->Push(aAudio);
|
||||
mMaster->PushAudio(aAudio);
|
||||
}
|
||||
|
||||
void HandleVideoDecoded(MediaData* aVideo, TimeStamp aDecodeStart) override
|
||||
|
@ -1371,7 +1373,7 @@ private:
|
|||
MOZ_ASSERT(NeedMoreVideo());
|
||||
|
||||
if (aVideo->mTime > mCurrentTime) {
|
||||
mMaster->Push(aVideo);
|
||||
mMaster->PushVideo(aVideo);
|
||||
FinishSeek();
|
||||
} else {
|
||||
RequestVideoData();
|
||||
|
@ -1550,7 +1552,7 @@ public:
|
|||
{
|
||||
// This might be the sample we need to exit buffering.
|
||||
// Schedule Step() to check it.
|
||||
mMaster->Push(aAudio);
|
||||
mMaster->PushAudio(aAudio);
|
||||
mMaster->ScheduleStateMachine();
|
||||
}
|
||||
|
||||
|
@ -1558,7 +1560,7 @@ public:
|
|||
{
|
||||
// This might be the sample we need to exit buffering.
|
||||
// Schedule Step() to check it.
|
||||
mMaster->Push(aVideo);
|
||||
mMaster->PushVideo(aVideo);
|
||||
mMaster->ScheduleStateMachine();
|
||||
}
|
||||
|
||||
|
@ -2163,6 +2165,7 @@ BufferingState::Step()
|
|||
SLOG("Buffering: wait %ds, timeout in %.3lfs",
|
||||
mBufferingWait, mBufferingWait - elapsed.ToSeconds());
|
||||
mMaster->ScheduleStateMachineIn(USECS_PER_S);
|
||||
mMaster->DispatchDecodeTasksIfNeeded();
|
||||
return;
|
||||
}
|
||||
} else if (mMaster->OutOfDecodedAudio() || mMaster->OutOfDecodedVideo()) {
|
||||
|
@ -2549,25 +2552,20 @@ MediaDecoderStateMachine::NeedToDecodeAudio()
|
|||
}
|
||||
|
||||
void
|
||||
MediaDecoderStateMachine::Push(MediaData* aSample)
|
||||
MediaDecoderStateMachine::PushAudio(MediaData* aSample)
|
||||
{
|
||||
MOZ_ASSERT(OnTaskQueue());
|
||||
MOZ_ASSERT(aSample);
|
||||
AudioQueue().Push(aSample);
|
||||
}
|
||||
|
||||
if (aSample->mType == MediaData::AUDIO_DATA) {
|
||||
// TODO: Send aSample to MSG and recalculate readystate before pushing,
|
||||
// otherwise AdvanceFrame may pop the sample before we have a chance
|
||||
// to reach playing.
|
||||
AudioQueue().Push(aSample);
|
||||
} else if (aSample->mType == MediaData::VIDEO_DATA) {
|
||||
// TODO: Send aSample to MSG and recalculate readystate before pushing,
|
||||
// otherwise AdvanceFrame may pop the sample before we have a chance
|
||||
// to reach playing.
|
||||
aSample->As<VideoData>()->mFrameID = ++mCurrentFrameID;
|
||||
VideoQueue().Push(aSample);
|
||||
}
|
||||
|
||||
DispatchDecodeTasksIfNeeded();
|
||||
void
|
||||
MediaDecoderStateMachine::PushVideo(MediaData* aSample)
|
||||
{
|
||||
MOZ_ASSERT(OnTaskQueue());
|
||||
MOZ_ASSERT(aSample);
|
||||
aSample->As<VideoData>()->mFrameID = ++mCurrentFrameID;
|
||||
VideoQueue().Push(aSample);
|
||||
}
|
||||
|
||||
void
|
||||
|
|
|
@ -338,7 +338,8 @@ protected:
|
|||
|
||||
// Inserts MediaData* samples into their respective MediaQueues.
|
||||
// aSample must not be null.
|
||||
void Push(MediaData* aSample);
|
||||
void PushAudio(MediaData* aSample);
|
||||
void PushVideo(MediaData* aSample);
|
||||
|
||||
void OnAudioPopped(const RefPtr<MediaData>& aSample);
|
||||
void OnVideoPopped(const RefPtr<MediaData>& aSample);
|
||||
|
|
|
@ -17,7 +17,6 @@
|
|||
#include "HTMLEditRules.h"
|
||||
#include "HTMLEditUtils.h"
|
||||
#include "HTMLURIRefObject.h"
|
||||
#include "SetDocumentTitleTransaction.h"
|
||||
#include "StyleSheetTransactions.h"
|
||||
#include "TextEditUtils.h"
|
||||
#include "TypeInState.h"
|
||||
|
@ -811,22 +810,6 @@ HTMLEditor::IsBlockNode(nsINode* aNode)
|
|||
return aNode && NodeIsBlockStatic(aNode);
|
||||
}
|
||||
|
||||
// Non-static version for the nsIEditor interface and JavaScript
|
||||
NS_IMETHODIMP
|
||||
HTMLEditor::SetDocumentTitle(const nsAString& aTitle)
|
||||
{
|
||||
RefPtr<SetDocumentTitleTransaction> transaction =
|
||||
new SetDocumentTitleTransaction();
|
||||
NS_ENSURE_TRUE(transaction, NS_ERROR_OUT_OF_MEMORY);
|
||||
|
||||
nsresult rv = transaction->Init(this, &aTitle);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
//Don't let Rules System change the selection
|
||||
AutoTransactionsConserveSelection dontChangeSelection(this);
|
||||
return EditorBase::DoTransaction(transaction);
|
||||
}
|
||||
|
||||
/**
|
||||
* GetBlockNodeParent returns enclosing block level ancestor, if any.
|
||||
*/
|
||||
|
|
|
@ -1,230 +0,0 @@
|
|||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* 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/. */
|
||||
|
||||
#include "SetDocumentTitleTransaction.h"
|
||||
#include "mozilla/dom/Element.h" // for Element
|
||||
#include "nsAString.h"
|
||||
#include "nsCOMPtr.h" // for nsCOMPtr, getter_AddRefs, etc.
|
||||
#include "nsDebug.h" // for NS_ENSURE_SUCCESS, etc.
|
||||
#include "nsError.h" // for NS_OK, NS_ERROR_FAILURE, etc.
|
||||
#include "nsIDOMCharacterData.h" // for nsIDOMCharacterData
|
||||
#include "nsIDOMDocument.h" // for nsIDOMDocument
|
||||
#include "nsIDOMElement.h" // for nsIDOMElement
|
||||
#include "nsIDOMNode.h" // for nsIDOMNode
|
||||
#include "nsIDOMNodeList.h" // for nsIDOMNodeList
|
||||
#include "nsIDOMText.h" // for nsIDOMText
|
||||
#include "nsIDocument.h" // for nsIDocument
|
||||
#include "nsIEditor.h" // for nsIEditor
|
||||
#include "nsIHTMLEditor.h" // for nsIHTMLEditor
|
||||
#include "nsLiteralString.h" // for NS_LITERAL_STRING
|
||||
#include "nsTextNode.h" // for nsTextNode
|
||||
#include "nsQueryObject.h" // for do_QueryObject
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
// Note that aEditor is not refcounted.
|
||||
SetDocumentTitleTransaction::SetDocumentTitleTransaction()
|
||||
: mEditor(nullptr)
|
||||
, mIsTransient(false)
|
||||
{
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
SetDocumentTitleTransaction::Init(nsIHTMLEditor* aEditor,
|
||||
const nsAString* aValue)
|
||||
|
||||
{
|
||||
NS_ASSERTION(aEditor && aValue, "null args");
|
||||
if (!aEditor || !aValue) {
|
||||
return NS_ERROR_NULL_POINTER;
|
||||
}
|
||||
|
||||
mEditor = aEditor;
|
||||
mValue = *aValue;
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
SetDocumentTitleTransaction::DoTransaction()
|
||||
{
|
||||
return SetDomTitle(mValue);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
SetDocumentTitleTransaction::UndoTransaction()
|
||||
{
|
||||
// No extra work required; the DOM changes alone are enough
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
SetDocumentTitleTransaction::RedoTransaction()
|
||||
{
|
||||
// No extra work required; the DOM changes alone are enough
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
SetDocumentTitleTransaction::SetDomTitle(const nsAString& aTitle)
|
||||
{
|
||||
nsCOMPtr<nsIEditor> editor = do_QueryInterface(mEditor);
|
||||
if (NS_WARN_IF(!editor)) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIDOMDocument> domDoc;
|
||||
nsresult rv = editor->GetDocument(getter_AddRefs(domDoc));
|
||||
if (NS_WARN_IF(!domDoc)) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIDOMNodeList> titleList;
|
||||
rv = domDoc->GetElementsByTagName(NS_LITERAL_STRING("title"),
|
||||
getter_AddRefs(titleList));
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
// First assume we will NOT really do anything
|
||||
// (transaction will not be pushed on stack)
|
||||
mIsTransient = true;
|
||||
|
||||
nsCOMPtr<nsIDOMNode> titleNode;
|
||||
if(titleList) {
|
||||
rv = titleList->Item(0, getter_AddRefs(titleNode));
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
if (titleNode) {
|
||||
// Delete existing child textnode of title node
|
||||
// (Note: all contents under a TITLE node are always in a single text node)
|
||||
nsCOMPtr<nsIDOMNode> child;
|
||||
rv = titleNode->GetFirstChild(getter_AddRefs(child));
|
||||
if (NS_FAILED(rv)) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
if(child) {
|
||||
// Save current text as the undo value
|
||||
nsCOMPtr<nsIDOMCharacterData> textNode = do_QueryInterface(child);
|
||||
if(textNode) {
|
||||
textNode->GetData(mUndoValue);
|
||||
|
||||
// If title text is identical to what already exists,
|
||||
// quit now (mIsTransient is now TRUE)
|
||||
if (mUndoValue == aTitle) {
|
||||
return NS_OK;
|
||||
}
|
||||
}
|
||||
rv = editor->DeleteNode(child);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// We didn't return above, thus we really will be changing the title
|
||||
mIsTransient = false;
|
||||
|
||||
// Get the <HEAD> node, create a <TITLE> and insert it under the HEAD
|
||||
nsCOMPtr<nsIDocument> document = do_QueryInterface(domDoc);
|
||||
if (NS_WARN_IF(!document)) {
|
||||
return NS_ERROR_UNEXPECTED;
|
||||
}
|
||||
|
||||
RefPtr<dom::Element> headElement = document->GetHeadElement();
|
||||
if (NS_WARN_IF(!headElement)) {
|
||||
return NS_ERROR_UNEXPECTED;
|
||||
}
|
||||
|
||||
bool newTitleNode = false;
|
||||
uint32_t newTitleIndex = 0;
|
||||
|
||||
if (!titleNode) {
|
||||
// Didn't find one above: Create a new one
|
||||
nsCOMPtr<nsIDOMElement>titleElement;
|
||||
rv = domDoc->CreateElement(NS_LITERAL_STRING("title"),
|
||||
getter_AddRefs(titleElement));
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
}
|
||||
if (NS_WARN_IF(!titleElement)) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
titleNode = do_QueryInterface(titleElement);
|
||||
newTitleNode = true;
|
||||
|
||||
// Get index so we append new title node after all existing HEAD children.
|
||||
newTitleIndex = headElement->GetChildCount();
|
||||
}
|
||||
|
||||
// Append a text node under the TITLE only if the title text isn't empty.
|
||||
if (titleNode && !aTitle.IsEmpty()) {
|
||||
RefPtr<nsTextNode> textNode = document->CreateTextNode(aTitle);
|
||||
|
||||
if (newTitleNode) {
|
||||
// Not undoable: We will insert newTitleNode below
|
||||
nsCOMPtr<nsINode> title = do_QueryInterface(titleNode);
|
||||
MOZ_ASSERT(title);
|
||||
|
||||
ErrorResult result;
|
||||
title->AppendChild(*textNode, result);
|
||||
if (NS_WARN_IF(result.Failed())) {
|
||||
return result.StealNSResult();
|
||||
}
|
||||
} else {
|
||||
// This is an undoable transaction
|
||||
nsCOMPtr<nsIDOMNode> newNode = do_QueryObject(textNode);
|
||||
if (NS_WARN_IF(!newNode)) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
rv = editor->InsertNode(newNode, titleNode, 0);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
}
|
||||
}
|
||||
// Calling AppendChild() or InsertNode() could cause removing the head
|
||||
// element. So, let's mark it dirty.
|
||||
headElement = nullptr;
|
||||
}
|
||||
|
||||
if (newTitleNode) {
|
||||
if (!headElement) {
|
||||
headElement = document->GetHeadElement();
|
||||
if (NS_WARN_IF(!headElement)) {
|
||||
// XXX Can we return NS_OK when there is no head element?
|
||||
return NS_ERROR_UNEXPECTED;
|
||||
}
|
||||
}
|
||||
// Undoable transaction to insert title+text together
|
||||
rv = editor->InsertNode(titleNode, headElement->AsDOMNode(), newTitleIndex);
|
||||
}
|
||||
return rv;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
SetDocumentTitleTransaction::GetTxnDescription(nsAString& aString)
|
||||
{
|
||||
aString.AssignLiteral("SetDocumentTitleTransaction: ");
|
||||
aString += mValue;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
SetDocumentTitleTransaction::GetIsTransient(bool* aIsTransient)
|
||||
{
|
||||
if (NS_WARN_IF(!aIsTransient)) {
|
||||
return NS_ERROR_NULL_POINTER;
|
||||
}
|
||||
*aIsTransient = mIsTransient;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
} // namespace mozilla
|
|
@ -1,60 +0,0 @@
|
|||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef SetDocumentTitleTransaction_h
|
||||
#define SetDocumentTitleTransaction_h
|
||||
|
||||
#include "mozilla/EditTransactionBase.h" // for EditTransactionBase, etc.
|
||||
#include "nsString.h" // for nsString
|
||||
#include "nscore.h" // for NS_IMETHOD, nsAString, etc.
|
||||
|
||||
class nsIHTMLEditor;
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
/**
|
||||
* A transaction that changes the document's title,
|
||||
* which is a text node under the <title> tag in a page's <head> section
|
||||
* provides default concrete behavior for all nsITransaction methods.
|
||||
*/
|
||||
class SetDocumentTitleTransaction final : public EditTransactionBase
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* Initialize the transaction.
|
||||
* @param aEditor The object providing core editing operations.
|
||||
* @param aValue The new value for document title.
|
||||
*/
|
||||
NS_IMETHOD Init(nsIHTMLEditor* aEditor,
|
||||
const nsAString* aValue);
|
||||
SetDocumentTitleTransaction();
|
||||
|
||||
private:
|
||||
nsresult SetDomTitle(const nsAString& aTitle);
|
||||
|
||||
public:
|
||||
NS_DECL_EDITTRANSACTIONBASE
|
||||
|
||||
NS_IMETHOD RedoTransaction() override;
|
||||
NS_IMETHOD GetIsTransient(bool *aIsTransient) override;
|
||||
|
||||
protected:
|
||||
|
||||
// The editor that created this transaction.
|
||||
nsIHTMLEditor* mEditor;
|
||||
|
||||
// The new title string.
|
||||
nsString mValue;
|
||||
|
||||
// The previous title string to use for undo.
|
||||
nsString mUndoValue;
|
||||
|
||||
// Set true if we dont' really change the title during Do().
|
||||
bool mIsTransient;
|
||||
};
|
||||
|
||||
} // namespace mozilla
|
||||
|
||||
#endif // #ifndef SetDocumentTitleTransaction_h
|
|
@ -64,7 +64,6 @@ UNIFIED_SOURCES += [
|
|||
'JoinNodeTransaction.cpp',
|
||||
'PlaceholderTransaction.cpp',
|
||||
'SelectionState.cpp',
|
||||
'SetDocumentTitleTransaction.cpp',
|
||||
'SplitNodeTransaction.cpp',
|
||||
'StyleSheetTransactions.cpp',
|
||||
'TextEditor.cpp',
|
||||
|
|
|
@ -9,6 +9,5 @@ support-files = green.png
|
|||
[test_bug780908.xul]
|
||||
[test_contenteditable_text_input_handling.html]
|
||||
[test_htmleditor_keyevent_handling.html]
|
||||
[test_set_document_title_transaction.html]
|
||||
[test_texteditor_keyevent_handling.html]
|
||||
skip-if = (debug && os=='win') || (os == 'linux') # Bug 1116205, leaks on windows debug, fails delete key on linux
|
||||
|
|
|
@ -1,79 +0,0 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Test for SetDocumentTitleTransaction</title>
|
||||
<script type="text/javascript"
|
||||
src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<link rel="stylesheet" type="text/css"
|
||||
href="chrome://mochikit/content/tests/SimpleTest/test.css" />
|
||||
</head>
|
||||
<body onload="runTests()">
|
||||
<div id="display">
|
||||
<iframe src="data:text/html,<!DOCTYPE html><html><head><title>first title</title></head><body></body></html>"></iframe>
|
||||
</div>
|
||||
<div id="content" style="display: none">
|
||||
</div>
|
||||
<pre id="test">
|
||||
</pre>
|
||||
|
||||
<script class="testbody" type="application/javascript">
|
||||
function runTests() {
|
||||
var iframe = document.getElementsByTagName("iframe")[0];
|
||||
function isDocumentTitleEquals(aDescription, aExpectedTitle) {
|
||||
is(iframe.contentDocument.title, aExpectedTitle, aDescription + ": document.title should be " + aExpectedTitle);
|
||||
is(iframe.contentDocument.getElementsByTagName("title")[0].textContent, aExpectedTitle, aDescription + ": The text in the title element should be " + aExpectedTitle);
|
||||
}
|
||||
|
||||
isDocumentTitleEquals("Checking isDocumentTitleEquals()", "first title");
|
||||
|
||||
const kTests = [
|
||||
{ description: "designMode=\"on\"",
|
||||
init: function () {
|
||||
iframe.contentDocument.designMode = "on";
|
||||
},
|
||||
cleanUp: function () {
|
||||
iframe.contentDocument.designMode = "off";
|
||||
}
|
||||
},
|
||||
{ description: "html element has contenteditable attribute",
|
||||
init: function () {
|
||||
iframe.contentDocument.documentElement.setAttribute("contenteditable", "true");
|
||||
},
|
||||
cleanUp: function () {
|
||||
iframe.contentDocument.documentElement.removeAttribute("contenteditable");
|
||||
}
|
||||
},
|
||||
];
|
||||
|
||||
for (var i = 0; i < kTests.length; i++) {
|
||||
const kTest = kTests[i];
|
||||
kTest.init();
|
||||
|
||||
var editor = SpecialPowers.wrap(iframe.contentWindow).
|
||||
QueryInterface(SpecialPowers.Ci.nsIInterfaceRequestor).
|
||||
getInterface(SpecialPowers.Ci.nsIWebNavigation).
|
||||
QueryInterface(SpecialPowers.Ci.nsIDocShell).editor;
|
||||
ok(editor, kTest.description + ": The docshell should have editor");
|
||||
var htmlEditor = editor.QueryInterface(SpecialPowers.Ci.nsIHTMLEditor);
|
||||
ok(htmlEditor, kTest.description + ": The editor should have nsIHTMLEditor interface");
|
||||
|
||||
// Replace existing title.
|
||||
htmlEditor.setDocumentTitle("Modified title");
|
||||
isDocumentTitleEquals(kTest.description, "Modified title");
|
||||
|
||||
// When the document doesn't have <title> element, title element should be created automatically.
|
||||
iframe.contentDocument.head.removeChild(iframe.contentDocument.getElementsByTagName("title")[0]);
|
||||
is(iframe.contentDocument.getElementsByTagName("title").length, 0, kTest.description + ": There should be no title element");
|
||||
htmlEditor.setDocumentTitle("new title");
|
||||
is(iframe.contentDocument.getElementsByTagName("title").length, 1, kTest.description + ": There should be a title element");
|
||||
isDocumentTitleEquals(kTest.description, "new title");
|
||||
|
||||
kTest.cleanUp();
|
||||
}
|
||||
|
||||
SimpleTest.finish();
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
|
||||
</html>
|
|
@ -236,11 +236,6 @@ interface nsIHTMLEditor : nsISupports
|
|||
void insertElementAtSelection(in nsIDOMElement aElement,
|
||||
in boolean aDeleteSelection);
|
||||
|
||||
/**
|
||||
* Set the documents title.
|
||||
*/
|
||||
void setDocumentTitle(in AString aTitle);
|
||||
|
||||
/**
|
||||
* Set the BaseURL for the document to the current URL
|
||||
* but only if the page doesn't have a <base> tag
|
||||
|
|
|
@ -1882,8 +1882,7 @@ nsIFrame::DisplayCaret(nsDisplayListBuilder* aBuilder,
|
|||
nscolor
|
||||
nsIFrame::GetCaretColorAt(int32_t aOffset)
|
||||
{
|
||||
// Use text color.
|
||||
return StyleColor()->mColor;
|
||||
return StyleColor()->CalcComplexColor(StyleUserInterface()->mCaretColor);
|
||||
}
|
||||
|
||||
bool
|
||||
|
|
|
@ -0,0 +1,18 @@
|
|||
<!DOCTYPE html>
|
||||
<style>
|
||||
@font-face {
|
||||
font-family: Ahem;
|
||||
src: url(../fonts/Ahem.ttf);
|
||||
}
|
||||
div {
|
||||
font: 16px/1 Ahem;
|
||||
width: 1em;
|
||||
background: green;
|
||||
}
|
||||
</style>
|
||||
<div><span></span></div>
|
||||
<script>
|
||||
let $div = document.querySelector('div');
|
||||
let $span = document.querySelector('span');
|
||||
$div.style.height = $span.getBoundingClientRect().height + 'px';
|
||||
</script>
|
|
@ -0,0 +1,17 @@
|
|||
<!DOCTYPE html>
|
||||
<style>
|
||||
@font-face {
|
||||
font-family: Ahem;
|
||||
src: url(../fonts/Ahem.ttf);
|
||||
}
|
||||
textarea {
|
||||
caret-color: green;
|
||||
font: 16px/1 Ahem;
|
||||
outline: none;
|
||||
border: 0 none;
|
||||
resize: none;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
}
|
||||
</style>
|
||||
<textarea autofocus></textarea>
|
|
@ -0,0 +1 @@
|
|||
pref(ui.caretWidth,16) needs-focus == caret-color-01.html caret-color-01-ref.html
|
|
@ -130,6 +130,9 @@ include css-sizing/reftest.list
|
|||
# css transitions
|
||||
include css-transitions/reftest.list
|
||||
|
||||
# css ui
|
||||
include css-ui/reftest.list
|
||||
|
||||
# css :-moz-ui-invalid
|
||||
include css-ui-invalid/reftest.list
|
||||
|
||||
|
|
|
@ -4730,8 +4730,12 @@ StyleAnimationValue::ExtractComputedValue(nsCSSPropertyID aProperty,
|
|||
StyleDataAtOffset<nscolor>(styleStruct, ssOffset));
|
||||
return true;
|
||||
case eStyleAnimType_ComplexColor: {
|
||||
aComputedValue.SetComplexColorValue(
|
||||
StyleDataAtOffset<StyleComplexColor>(styleStruct, ssOffset));
|
||||
auto& color = StyleDataAtOffset<StyleComplexColor>(styleStruct, ssOffset);
|
||||
if (color.mIsAuto) {
|
||||
aComputedValue.SetAutoValue();
|
||||
} else {
|
||||
aComputedValue.SetComplexColorValue(color);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
case eStyleAnimType_PaintServer: {
|
||||
|
@ -5048,7 +5052,9 @@ StyleAnimationValue::SetCurrentColorValue()
|
|||
void
|
||||
StyleAnimationValue::SetComplexColorValue(const StyleComplexColor& aColor)
|
||||
{
|
||||
if (aColor.IsCurrentColor()) {
|
||||
if (aColor.mIsAuto) {
|
||||
SetAutoValue();
|
||||
} else if (aColor.IsCurrentColor()) {
|
||||
SetCurrentColorValue();
|
||||
} else if (aColor.IsNumericColor()) {
|
||||
SetColorValue(aColor.mColor);
|
||||
|
|
|
@ -24,16 +24,29 @@ struct StyleComplexColor
|
|||
{
|
||||
nscolor mColor;
|
||||
uint8_t mForegroundRatio;
|
||||
// Whether the complex color represents a computed-value time auto
|
||||
// value. This is only a flag indicating that this value should not
|
||||
// be interpolatable with other colors, while other fields still
|
||||
// represents the actual used color of this value.
|
||||
bool mIsAuto;
|
||||
|
||||
static StyleComplexColor FromColor(nscolor aColor) { return {aColor, 0}; }
|
||||
static StyleComplexColor CurrentColor() { return {NS_RGBA(0, 0, 0, 0), 255}; }
|
||||
static StyleComplexColor FromColor(nscolor aColor) {
|
||||
return {aColor, 0, false};
|
||||
}
|
||||
static StyleComplexColor CurrentColor() {
|
||||
return {NS_RGBA(0, 0, 0, 0), 255, false};
|
||||
}
|
||||
static StyleComplexColor Auto() {
|
||||
return {NS_RGBA(0, 0, 0, 0), 255, true};
|
||||
}
|
||||
|
||||
bool IsNumericColor() const { return mForegroundRatio == 0; }
|
||||
bool IsCurrentColor() const { return mForegroundRatio == 255; }
|
||||
|
||||
bool operator==(const StyleComplexColor& aOther) const {
|
||||
return mForegroundRatio == aOther.mForegroundRatio &&
|
||||
(IsCurrentColor() || mColor == aOther.mColor);
|
||||
(IsCurrentColor() || mColor == aOther.mColor) &&
|
||||
mIsAuto == aOther.mIsAuto;
|
||||
}
|
||||
bool operator!=(const StyleComplexColor& aOther) const {
|
||||
return !(*this == aOther);
|
||||
|
|
|
@ -1381,6 +1381,17 @@ CSS_PROP_TABLEBORDER(
|
|||
kCaptionSideKTable,
|
||||
CSS_PROP_NO_OFFSET,
|
||||
eStyleAnimType_Discrete)
|
||||
CSS_PROP_USERINTERFACE(
|
||||
caret-color,
|
||||
caret_color,
|
||||
CaretColor,
|
||||
CSS_PROPERTY_PARSE_VALUE |
|
||||
CSS_PROPERTY_IGNORED_WHEN_COLORS_DISABLED,
|
||||
"",
|
||||
VARIANT_AUTO | VARIANT_HC,
|
||||
nullptr,
|
||||
offsetof(nsStyleUserInterface, mCaretColor),
|
||||
eStyleAnimType_ComplexColor)
|
||||
CSS_PROP_DISPLAY(
|
||||
clear,
|
||||
clear,
|
||||
|
|
|
@ -4143,6 +4143,14 @@ nsComputedDOMStyle::DoGetUnicodeBidi()
|
|||
return val.forget();
|
||||
}
|
||||
|
||||
already_AddRefed<CSSValue>
|
||||
nsComputedDOMStyle::DoGetCaretColor()
|
||||
{
|
||||
RefPtr<nsROCSSPrimitiveValue> val = new nsROCSSPrimitiveValue;
|
||||
SetValueFromComplexColor(val, StyleUserInterface()->mCaretColor);
|
||||
return val.forget();
|
||||
}
|
||||
|
||||
already_AddRefed<CSSValue>
|
||||
nsComputedDOMStyle::DoGetCursor()
|
||||
{
|
||||
|
|
|
@ -482,6 +482,7 @@ private:
|
|||
already_AddRefed<CSSValue> DoGetShapeOutside();
|
||||
|
||||
/* User interface properties */
|
||||
already_AddRefed<CSSValue> DoGetCaretColor();
|
||||
already_AddRefed<CSSValue> DoGetCursor();
|
||||
already_AddRefed<CSSValue> DoGetForceBrokenImageIcon();
|
||||
already_AddRefed<CSSValue> DoGetIMEMode();
|
||||
|
|
|
@ -101,6 +101,7 @@ COMPUTED_STYLE_PROP(box_decoration_break, BoxDecorationBreak)
|
|||
COMPUTED_STYLE_PROP(box_shadow, BoxShadow)
|
||||
COMPUTED_STYLE_PROP(box_sizing, BoxSizing)
|
||||
COMPUTED_STYLE_PROP(caption_side, CaptionSide)
|
||||
COMPUTED_STYLE_PROP(caret_color, CaretColor)
|
||||
COMPUTED_STYLE_PROP(clear, Clear)
|
||||
COMPUTED_STYLE_PROP(clip, Clip)
|
||||
COMPUTED_STYLE_PROP(color, Color)
|
||||
|
|
|
@ -1142,13 +1142,16 @@ SetComplexColor(const nsCSSValue& aValue,
|
|||
aResult = StyleComplexColor::CurrentColor();
|
||||
} else if (unit == eCSSUnit_ComplexColor) {
|
||||
aResult = aValue.GetStyleComplexColorValue();
|
||||
} else if (unit == eCSSUnit_Auto) {
|
||||
aResult = StyleComplexColor::Auto();
|
||||
} else {
|
||||
nscolor resultColor;
|
||||
if (!SetColor(aValue, aParentColor.mColor, aPresContext,
|
||||
nullptr, aResult.mColor, aConditions)) {
|
||||
nullptr, resultColor, aConditions)) {
|
||||
MOZ_ASSERT_UNREACHABLE("Unknown color value");
|
||||
return;
|
||||
}
|
||||
aResult.mForegroundRatio = 0;
|
||||
aResult = StyleComplexColor::FromColor(resultColor);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -5293,6 +5296,14 @@ nsRuleNode::ComputeUserInterfaceData(void* aStartStruct,
|
|||
parentUI->mPointerEvents,
|
||||
NS_STYLE_POINTER_EVENTS_AUTO);
|
||||
|
||||
// caret-color: auto, color, inherit
|
||||
const nsCSSValue* caretColorValue = aRuleData->ValueForCaretColor();
|
||||
SetComplexColor<eUnsetInherit>(*caretColorValue,
|
||||
parentUI->mCaretColor,
|
||||
StyleComplexColor::Auto(),
|
||||
mPresContext,
|
||||
ui->mCaretColor, conditions);
|
||||
|
||||
COMPUTE_END_INHERITED(UserInterface, ui)
|
||||
}
|
||||
|
||||
|
|
|
@ -3974,6 +3974,7 @@ nsStyleUserInterface::nsStyleUserInterface(StyleStructContext aContext)
|
|||
, mUserFocus(StyleUserFocus::None)
|
||||
, mPointerEvents(NS_STYLE_POINTER_EVENTS_AUTO)
|
||||
, mCursor(NS_STYLE_CURSOR_AUTO)
|
||||
, mCaretColor(StyleComplexColor::Auto())
|
||||
{
|
||||
MOZ_COUNT_CTOR(nsStyleUserInterface);
|
||||
}
|
||||
|
@ -3985,6 +3986,7 @@ nsStyleUserInterface::nsStyleUserInterface(const nsStyleUserInterface& aSource)
|
|||
, mPointerEvents(aSource.mPointerEvents)
|
||||
, mCursor(aSource.mCursor)
|
||||
, mCursorImages(aSource.mCursorImages)
|
||||
, mCaretColor(aSource.mCaretColor)
|
||||
{
|
||||
MOZ_COUNT_CTOR(nsStyleUserInterface);
|
||||
}
|
||||
|
@ -4046,6 +4048,10 @@ nsStyleUserInterface::CalcDifference(const nsStyleUserInterface& aNewData) const
|
|||
hint |= nsChangeHint_NeutralChange;
|
||||
}
|
||||
|
||||
if (mCaretColor != aNewData.mCaretColor) {
|
||||
hint |= nsChangeHint_RepaintFrame;
|
||||
}
|
||||
|
||||
return hint;
|
||||
}
|
||||
|
||||
|
|
|
@ -3400,6 +3400,7 @@ struct MOZ_NEEDS_MEMMOVABLE_MEMBERS nsStyleUserInterface
|
|||
|
||||
uint8_t mCursor; // [inherited] See nsStyleConsts.h
|
||||
nsTArray<nsCursorImage> mCursorImages; // [inherited] images and coords
|
||||
mozilla::StyleComplexColor mCaretColor; // [inherited]
|
||||
|
||||
inline uint8_t GetEffectivePointerEvents(nsIFrame* aFrame) const;
|
||||
};
|
||||
|
|
|
@ -2836,6 +2836,18 @@ var gCSSProperties = {
|
|||
other_values: [ "bottom", "left", "right", "top-outside", "bottom-outside" ],
|
||||
invalid_values: []
|
||||
},
|
||||
"caret-color": {
|
||||
domProp: "caretColor",
|
||||
inherited: true,
|
||||
type: CSS_TYPE_LONGHAND,
|
||||
prerequisites: { "color": "black" },
|
||||
// Though "auto" is an independent computed-value time keyword value,
|
||||
// it is not distinguishable from currentcolor because getComputedStyle
|
||||
// always returns used value for <color>.
|
||||
initial_values: [ "auto", "currentcolor", "black", "rgb(0,0,0)" ],
|
||||
other_values: [ "green", "transparent", "rgba(128,128,128,.5)", "#123" ],
|
||||
invalid_values: [ "#0", "#00", "#00000", "cc00ff" ]
|
||||
},
|
||||
"clear": {
|
||||
domProp: "clear",
|
||||
inherited: false,
|
||||
|
|
|
@ -129,6 +129,9 @@ var supported_properties = {
|
|||
"bottom": [ test_length_transition, test_percent_transition,
|
||||
test_length_percent_calc_transition,
|
||||
test_length_unclamped, test_percent_unclamped ],
|
||||
"caret-color": [ test_color_transition,
|
||||
test_true_currentcolor_transition,
|
||||
test_auto_color_transition ],
|
||||
"clip": [ test_rect_transition ],
|
||||
"clip-path": [ test_clip_path_transition ],
|
||||
"color": [ test_color_transition,
|
||||
|
@ -1379,6 +1382,21 @@ function test_true_currentcolor_transition(prop, get_color=(x => x), is_shorthan
|
|||
div.style.removeProperty("color");
|
||||
}
|
||||
|
||||
function test_auto_color_transition(prop, get_color=(x => x), is_shorthand=false) {
|
||||
const msg_prefix = `color-valued property ${prop}: `;
|
||||
const test_color = "rgb(51, 102, 153)";
|
||||
div.style.setProperty("transition-property", "none", "");
|
||||
div.style.setProperty(prop, "auto", "");
|
||||
let used_value_of_auto = get_color(cs.getPropertyValue(prop));
|
||||
isnot(used_value_of_auto, test_color,
|
||||
msg_prefix + "ensure used auto value is different than our test color");
|
||||
|
||||
div.style.setProperty("transition-property", prop, "");
|
||||
div.style.setProperty(prop, test_color, "");
|
||||
is(get_color(cs.getPropertyValue(prop)), test_color,
|
||||
msg_prefix + "not interpolatable between auto and rgb color");
|
||||
}
|
||||
|
||||
function get_color_from_shorthand_value(value) {
|
||||
var m = value.match(/rgba?\([^, ]*, [^, ]*, [^, ]*(?:, [^, ]*)?\)/);
|
||||
isnot(m, null, "shorthand property value should contain color");
|
||||
|
|
|
@ -8,6 +8,7 @@ user_pref("gfx.color_management.force_srgb", true);
|
|||
user_pref("gfx.logging.level", 1);
|
||||
user_pref("browser.dom.window.dump.enabled", true);
|
||||
user_pref("ui.caretBlinkTime", -1);
|
||||
user_pref("ui.caretWidth", 1);
|
||||
user_pref("dom.send_after_paint_to_content", true);
|
||||
// no slow script dialogs
|
||||
user_pref("dom.max_script_run_time", 0);
|
||||
|
|
|
@ -6,9 +6,7 @@ package org.mozilla.gecko.home;
|
|||
|
||||
import android.content.Context;
|
||||
import android.database.Cursor;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.support.v4.content.ContextCompat;
|
||||
import android.support.v4.graphics.drawable.DrawableCompat;
|
||||
import android.support.v7.widget.RecyclerView;
|
||||
import android.util.Log;
|
||||
import android.view.View;
|
||||
|
@ -123,10 +121,6 @@ public abstract class CombinedHistoryItem extends RecyclerView.ViewHolder {
|
|||
nameView.setTextColor(ContextCompat.getColor(context, isCollapsed ? R.color.tabs_tray_icon_grey : R.color.placeholder_active_grey));
|
||||
if (client.tabs.size() > 0) {
|
||||
deviceExpanded.setImageResource(isCollapsed ? R.drawable.home_group_collapsed : R.drawable.arrow_down);
|
||||
Drawable expandedDrawable = deviceExpanded.getDrawable();
|
||||
if (expandedDrawable != null) {
|
||||
DrawableCompat.setAutoMirrored(expandedDrawable, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -10,8 +10,6 @@ import java.util.concurrent.Future;
|
|||
|
||||
import android.content.Context;
|
||||
import android.database.Cursor;
|
||||
import android.support.v4.app.ActivityCompat;
|
||||
import android.support.v4.graphics.drawable.DrawableCompat;
|
||||
import android.support.v4.view.ViewCompat;
|
||||
import android.support.v4.widget.TextViewCompat;
|
||||
import android.text.TextUtils;
|
||||
|
@ -167,9 +165,6 @@ public class TwoLinePageRow extends LinearLayout
|
|||
}
|
||||
|
||||
mSwitchToTabIconId = iconId;
|
||||
if (mSwitchToTabIconId != 0) {
|
||||
DrawableCompat.setAutoMirrored(ActivityCompat.getDrawable(getContext(), mSwitchToTabIconId), true);
|
||||
}
|
||||
TextViewCompat.setCompoundDrawablesRelativeWithIntrinsicBounds(mUrl, mSwitchToTabIconId, 0, 0, 0);
|
||||
}
|
||||
|
||||
|
|
|
@ -10,7 +10,6 @@ import org.mozilla.gecko.widget.themed.ThemedImageButton;
|
|||
|
||||
import android.content.Context;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.support.v4.graphics.drawable.DrawableCompat;
|
||||
import android.util.AttributeSet;
|
||||
|
||||
public class MenuItemActionBar extends ThemedImageButton
|
||||
|
@ -47,10 +46,6 @@ public class MenuItemActionBar extends ThemedImageButton
|
|||
setVisibility(VISIBLE);
|
||||
setImageDrawable(icon);
|
||||
}
|
||||
icon = getDrawable();
|
||||
if (icon != null) {
|
||||
DrawableCompat.setAutoMirrored(icon, true);
|
||||
}
|
||||
}
|
||||
|
||||
void setIcon(int icon) {
|
||||
|
|
|
@ -11,7 +11,6 @@ import android.content.Context;
|
|||
import android.content.res.Resources;
|
||||
import android.graphics.Rect;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.support.v4.graphics.drawable.DrawableCompat;
|
||||
import android.support.v4.widget.TextViewCompat;
|
||||
import android.util.AttributeSet;
|
||||
import android.widget.TextView;
|
||||
|
@ -53,8 +52,6 @@ public class MenuItemDefault extends TextView
|
|||
|
||||
mState = res.getDrawable(R.drawable.menu_item_state).mutate();
|
||||
mState.setBounds(stateIconBounds);
|
||||
// Support RTL
|
||||
DrawableCompat.setAutoMirrored(mState, true);
|
||||
|
||||
if (sIconBounds == null) {
|
||||
int iconSize = res.getDimensionPixelSize(R.dimen.menu_item_icon);
|
||||
|
@ -118,9 +115,8 @@ public class MenuItemDefault extends TextView
|
|||
public void setEnabled(boolean enabled) {
|
||||
super.setEnabled(enabled);
|
||||
|
||||
if (mIcon != null) {
|
||||
if (mIcon != null)
|
||||
mIcon.setAlpha(enabled ? 255 : 99);
|
||||
}
|
||||
|
||||
if (mState != null)
|
||||
mState.setAlpha(enabled ? 255 : 99);
|
||||
|
|
|
@ -8,10 +8,7 @@ package org.mozilla.gecko.preferences;
|
|||
import org.mozilla.gecko.R;
|
||||
|
||||
import android.content.Context;
|
||||
import android.support.v4.graphics.drawable.DrawableCompat;
|
||||
import android.util.AttributeSet;
|
||||
import android.view.View;
|
||||
import android.widget.ImageView;
|
||||
|
||||
class AlignRightLinkPreference extends LinkPreference {
|
||||
|
||||
|
@ -24,11 +21,4 @@ class AlignRightLinkPreference extends LinkPreference {
|
|||
super(context, attrs, defStyle);
|
||||
setLayoutResource(R.layout.preference_rightalign_icon);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onBindView(View view) {
|
||||
super.onBindView(view);
|
||||
ImageView imageView = (ImageView) view.findViewById(R.id.menu_icon_more);
|
||||
DrawableCompat.setAutoMirrored(imageView.getDrawable(), true);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -13,7 +13,6 @@ import android.graphics.drawable.Drawable;
|
|||
import android.graphics.Bitmap;
|
||||
import android.graphics.drawable.BitmapDrawable;
|
||||
import android.support.v4.content.ContextCompat;
|
||||
import android.support.v4.graphics.drawable.DrawableCompat;
|
||||
import android.support.v4.view.ViewCompat;
|
||||
import android.support.v4.widget.TextViewCompat;
|
||||
import android.view.LayoutInflater;
|
||||
|
@ -85,10 +84,9 @@ public class PromptListAdapter extends ArrayAdapter<PromptListItem> {
|
|||
return VIEW_TYPE_COUNT;
|
||||
}
|
||||
|
||||
private Drawable getMoreDrawable(Context context) {
|
||||
private Drawable getMoreDrawable(Resources res) {
|
||||
if (mMoreDrawable == null) {
|
||||
mMoreDrawable = ContextCompat.getDrawable(context, R.drawable.menu_item_more);
|
||||
DrawableCompat.setAutoMirrored(mMoreDrawable, true);
|
||||
mMoreDrawable = res.getDrawable(R.drawable.menu_item_more);
|
||||
}
|
||||
return mMoreDrawable;
|
||||
}
|
||||
|
@ -127,7 +125,7 @@ public class PromptListAdapter extends ArrayAdapter<PromptListItem> {
|
|||
|
||||
Drawable moreDrawable = null;
|
||||
if (item.isParent) {
|
||||
moreDrawable = getMoreDrawable(getContext());
|
||||
moreDrawable = getMoreDrawable(res);
|
||||
}
|
||||
|
||||
if (d != null || moreDrawable != null) {
|
||||
|
|
|
@ -10,7 +10,6 @@ import android.content.Context;
|
|||
import android.content.res.TypedArray;
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.support.v4.graphics.drawable.DrawableCompat;
|
||||
import android.support.v4.view.ViewCompat;
|
||||
import android.util.AttributeSet;
|
||||
import android.view.ViewGroup;
|
||||
|
@ -34,8 +33,6 @@ public class TabPanelBackButton extends ImageButton {
|
|||
if (divider != null) {
|
||||
dividerWidth = divider.getIntrinsicWidth();
|
||||
}
|
||||
// Support RTL
|
||||
DrawableCompat.setAutoMirrored(getDrawable(), true);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -21,7 +21,6 @@ import android.content.res.Resources;
|
|||
import android.graphics.Canvas;
|
||||
import android.graphics.Paint;
|
||||
import android.graphics.Path;
|
||||
import android.support.v4.graphics.drawable.DrawableCompat;
|
||||
import android.support.v4.view.ViewCompat;
|
||||
import android.util.AttributeSet;
|
||||
import android.view.View;
|
||||
|
@ -51,8 +50,6 @@ abstract class BrowserToolbarPhoneBase extends BrowserToolbar {
|
|||
|
||||
// This will clip the translating edge's image at 60% of its width
|
||||
urlBarTranslatingEdge.getDrawable().setLevel(6000);
|
||||
// Support RTL
|
||||
DrawableCompat.setAutoMirrored(urlBarTranslatingEdge.getDrawable(), true);
|
||||
|
||||
editCancel = (ThemedImageView) findViewById(R.id.edit_cancel);
|
||||
|
||||
|
|
|
@ -17,7 +17,6 @@ import android.graphics.Path;
|
|||
import android.graphics.PorterDuff.Mode;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.graphics.drawable.StateListDrawable;
|
||||
import android.support.v4.graphics.drawable.DrawableCompat;
|
||||
import android.support.v4.view.ViewCompat;
|
||||
import android.util.AttributeSet;
|
||||
|
||||
|
@ -44,10 +43,6 @@ public class ShapedButton extends ThemedImageButton
|
|||
mCanvasDelegate = new CanvasDelegate(this, Mode.DST_IN, paint);
|
||||
|
||||
setWillNotDraw(false);
|
||||
Drawable drawable = getDrawable();
|
||||
if (drawable != null) {
|
||||
DrawableCompat.setAutoMirrored(drawable, true);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -17,7 +17,7 @@
|
|||
package org.mozilla.gecko.toolbar;
|
||||
|
||||
import android.support.v4.content.ContextCompat;
|
||||
import org.mozilla.gecko.AppConstants.Versions;
|
||||
|
||||
import org.mozilla.gecko.R;
|
||||
import org.mozilla.gecko.widget.themed.ThemedImageView;
|
||||
import org.mozilla.gecko.util.WeakReferenceHandler;
|
||||
|
@ -30,11 +30,9 @@ import android.graphics.Rect;
|
|||
import android.graphics.drawable.Drawable;
|
||||
import android.os.Handler;
|
||||
import android.os.Message;
|
||||
import android.support.v4.graphics.drawable.DrawableCompat;
|
||||
import android.support.v4.view.ViewCompat;
|
||||
import android.util.AttributeSet;
|
||||
import android.view.View;
|
||||
import android.view.animation.Animation;
|
||||
|
||||
/**
|
||||
* Progress view used for page loads.
|
||||
|
@ -88,7 +86,6 @@ public class ToolbarProgressView extends ThemedImageView {
|
|||
@Override
|
||||
public void onDraw(Canvas canvas) {
|
||||
final Drawable d = getDrawable();
|
||||
DrawableCompat.setAutoMirrored(d, true);
|
||||
d.setBounds(mBounds.getBounds());
|
||||
d.draw(canvas);
|
||||
}
|
||||
|
|
Двоичные данные
mobile/android/base/resources/drawable-ldrtl-hdpi/home_group_collapsed.png
Normal file
После Ширина: | Высота: | Размер: 268 B |
После Ширина: | Высота: | Размер: 288 B |
После Ширина: | Высота: | Размер: 289 B |
После Ширина: | Высота: | Размер: 109 B |
Двоичные данные
mobile/android/base/resources/drawable-ldrtl-hdpi/tabs_panel_nav_back.png
Normal file
После Ширина: | Высота: | Размер: 451 B |
Двоичные данные
mobile/android/base/resources/drawable-ldrtl-large-hdpi/ic_menu_back.png
Normal file
После Ширина: | Высота: | Размер: 351 B |
Двоичные данные
mobile/android/base/resources/drawable-ldrtl-large-hdpi/ic_menu_forward.png
Normal file
После Ширина: | Высота: | Размер: 468 B |
Двоичные данные
mobile/android/base/resources/drawable-ldrtl-large-xhdpi/ic_menu_back.png
Normal file
После Ширина: | Высота: | Размер: 393 B |
Двоичные данные
mobile/android/base/resources/drawable-ldrtl-large-xhdpi/ic_menu_forward.png
Normal file
После Ширина: | Высота: | Размер: 598 B |
Двоичные данные
mobile/android/base/resources/drawable-ldrtl-large-xxhdpi/ic_menu_back.png
Normal file
После Ширина: | Высота: | Размер: 544 B |
Двоичные данные
mobile/android/base/resources/drawable-ldrtl-large-xxhdpi/ic_menu_forward.png
Normal file
После Ширина: | Высота: | Размер: 842 B |
Двоичные данные
mobile/android/base/resources/drawable-ldrtl-xhdpi/home_group_collapsed.png
Normal file
После Ширина: | Высота: | Размер: 240 B |
После Ширина: | Высота: | Размер: 365 B |
После Ширина: | Высота: | Размер: 357 B |
После Ширина: | Высота: | Размер: 123 B |
Двоичные данные
mobile/android/base/resources/drawable-ldrtl-xhdpi/tabs_panel_nav_back.png
Normal file
После Ширина: | Высота: | Размер: 584 B |
Двоичные данные
mobile/android/base/resources/drawable-ldrtl-xxhdpi/home_group_collapsed.png
Normal file
После Ширина: | Высота: | Размер: 347 B |
Двоичные данные
mobile/android/base/resources/drawable-ldrtl-xxhdpi/tabs_panel_nav_back.png
Normal file
После Ширина: | Высота: | Размер: 808 B |
|
@ -631,7 +631,11 @@ pref("apz.axis_lock.breakout_threshold", "0.03125"); // 1/32 inches
|
|||
pref("apz.axis_lock.breakout_angle", "0.3926991"); // PI / 8 (22.5 degrees)
|
||||
pref("apz.axis_lock.direct_pan_angle", "1.047197"); // PI / 3 (60 degrees)
|
||||
pref("apz.content_response_timeout", 400);
|
||||
#ifdef NIGHTLY_BUILD
|
||||
pref("apz.drag.enabled", true);
|
||||
#else
|
||||
pref("apz.drag.enabled", false);
|
||||
#endif
|
||||
pref("apz.danger_zone_x", 50);
|
||||
pref("apz.danger_zone_y", 100);
|
||||
pref("apz.disable_for_scroll_linked_effects", false);
|
||||
|
|
|
@ -6,12 +6,25 @@ XPCOMUtils.defineLazyModuleGetter(this, "ExtensionStorage",
|
|||
"resource://gre/modules/ExtensionStorage.jsm");
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "ExtensionStorageSync",
|
||||
"resource://gre/modules/ExtensionStorageSync.jsm");
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "AddonManagerPrivate",
|
||||
"resource://gre/modules/AddonManager.jsm");
|
||||
|
||||
Cu.import("resource://gre/modules/ExtensionUtils.jsm");
|
||||
var {
|
||||
EventManager,
|
||||
ExtensionError,
|
||||
} = ExtensionUtils;
|
||||
|
||||
function enforceNoTemporaryAddon(extensionId) {
|
||||
const EXCEPTION_MESSAGE =
|
||||
"The storage API will not work with a temporary addon ID. " +
|
||||
"Please add an explicit addon ID to your manifest. " +
|
||||
"For more information see https://bugzil.la/1323228.";
|
||||
if (AddonManagerPrivate.isTemporaryInstallID(extensionId)) {
|
||||
throw new ExtensionError(EXCEPTION_MESSAGE);
|
||||
}
|
||||
}
|
||||
|
||||
function storageApiFactory(context) {
|
||||
let {extension} = context;
|
||||
return {
|
||||
|
@ -33,15 +46,19 @@ function storageApiFactory(context) {
|
|||
|
||||
sync: {
|
||||
get: function(spec) {
|
||||
enforceNoTemporaryAddon(extension.id);
|
||||
return ExtensionStorageSync.get(extension, spec, context);
|
||||
},
|
||||
set: function(items) {
|
||||
enforceNoTemporaryAddon(extension.id);
|
||||
return ExtensionStorageSync.set(extension, items, context);
|
||||
},
|
||||
remove: function(keys) {
|
||||
enforceNoTemporaryAddon(extension.id);
|
||||
return ExtensionStorageSync.remove(extension, keys, context);
|
||||
},
|
||||
clear: function() {
|
||||
enforceNoTemporaryAddon(extension.id);
|
||||
return ExtensionStorageSync.clear(extension, context);
|
||||
},
|
||||
},
|
||||
|
|
|
@ -5,6 +5,10 @@
|
|||
const STORAGE_SYNC_PREF = "webextensions.storage.sync.enabled";
|
||||
Cu.import("resource://gre/modules/Preferences.jsm");
|
||||
|
||||
add_task(function* setup() {
|
||||
yield ExtensionTestUtils.startAddonManager();
|
||||
});
|
||||
|
||||
/**
|
||||
* Utility function to ensure that all supported APIs for getting are
|
||||
* tested.
|
||||
|
@ -332,3 +336,34 @@ add_task(function* test_backgroundScript() {
|
|||
Preferences.reset(STORAGE_SYNC_PREF);
|
||||
yield extension.unload();
|
||||
});
|
||||
|
||||
add_task(function* test_storage_requires_real_id() {
|
||||
async function backgroundScript() {
|
||||
const EXCEPTION_MESSAGE =
|
||||
"The storage API is not available with a temporary addon ID. " +
|
||||
"Please add an explicit addon ID to your manifest. " +
|
||||
"For more information see https://bugzil.la/1323228.";
|
||||
|
||||
await browser.test.assertRejects(browser.storage.sync.set({"foo": "bar"}),
|
||||
EXCEPTION_MESSAGE);
|
||||
|
||||
browser.test.notifyPass("exception correct");
|
||||
}
|
||||
|
||||
let extensionData = {
|
||||
background: `(${backgroundScript})(${checkGetImpl})`,
|
||||
manifest: {
|
||||
permissions: ["storage"],
|
||||
},
|
||||
useAddonManager: "temporary",
|
||||
};
|
||||
|
||||
Preferences.set(STORAGE_SYNC_PREF, true);
|
||||
|
||||
let extension = ExtensionTestUtils.loadExtension(extensionData);
|
||||
yield extension.startup();
|
||||
yield extension.awaitFinish("exception correct");
|
||||
|
||||
Preferences.reset(STORAGE_SYNC_PREF);
|
||||
yield extension.unload();
|
||||
});
|
||||
|
|
|
@ -206,6 +206,7 @@ void
|
|||
nsFormFillController::NodeWillBeDestroyed(const nsINode* aNode)
|
||||
{
|
||||
mPwmgrInputs.Remove(aNode);
|
||||
mAutofillInputs.Remove(aNode);
|
||||
if (aNode == mListNode) {
|
||||
mListNode = nullptr;
|
||||
RevalidateDataList();
|
||||
|
@ -220,7 +221,7 @@ nsFormFillController::MaybeRemoveMutationObserver(nsINode* aNode)
|
|||
{
|
||||
// Nodes being tracked in mPwmgrInputs will have their observers removed when
|
||||
// they stop being tracked.
|
||||
if (!mPwmgrInputs.Get(aNode)) {
|
||||
if (!mPwmgrInputs.Get(aNode) && !mAutofillInputs.Get(aNode)) {
|
||||
aNode->RemoveMutationObserver(this);
|
||||
}
|
||||
}
|
||||
|
@ -294,6 +295,21 @@ nsFormFillController::MarkAsLoginManagerField(nsIDOMHTMLInputElement *aInput)
|
|||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsFormFillController::MarkAsAutofillField(nsIDOMHTMLInputElement *aInput)
|
||||
{
|
||||
/*
|
||||
* Support other components implementing form autofill and handle autocomplete
|
||||
* for the field.
|
||||
*/
|
||||
nsCOMPtr<nsINode> node = do_QueryInterface(aInput);
|
||||
NS_ENSURE_STATE(node);
|
||||
mAutofillInputs.Put(node, true);
|
||||
node->AddMutationObserverUnlessExists(this);
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////
|
||||
//// nsIAutoCompleteInput
|
||||
|
@ -509,6 +525,14 @@ nsFormFillController::GetSearchCount(uint32_t *aSearchCount)
|
|||
NS_IMETHODIMP
|
||||
nsFormFillController::GetSearchAt(uint32_t index, nsACString & _retval)
|
||||
{
|
||||
if (mAutofillInputs.Get(mFocusedInputNode)) {
|
||||
nsCOMPtr<nsIAutoCompleteSearch> profileSearch = do_GetService("@mozilla.org/autocomplete/search;1?name=autofill-profiles");
|
||||
if (profileSearch) {
|
||||
_retval.AssignLiteral("autofill-profiles");
|
||||
return NS_OK;
|
||||
}
|
||||
}
|
||||
|
||||
_retval.AssignLiteral("form-history");
|
||||
return NS_OK;
|
||||
}
|
||||
|
@ -908,6 +932,18 @@ nsFormFillController::RemoveForDocument(nsIDocument* aDoc)
|
|||
iter.Remove();
|
||||
}
|
||||
}
|
||||
|
||||
for (auto iter = mAutofillInputs.Iter(); !iter.Done(); iter.Next()) {
|
||||
const nsINode* key = iter.Key();
|
||||
if (key && (!aDoc || key->OwnerDoc() == aDoc)) {
|
||||
// mFocusedInputNode's observer is tracked separately, so don't remove it
|
||||
// here.
|
||||
if (key != mFocusedInputNode) {
|
||||
const_cast<nsINode*>(key)->RemoveMutationObserver(this);
|
||||
}
|
||||
iter.Remove();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
|
|
|
@ -111,6 +111,7 @@ protected:
|
|||
nsString mLastSearchString;
|
||||
|
||||
nsDataHashtable<nsPtrHashKey<const nsINode>, bool> mPwmgrInputs;
|
||||
nsDataHashtable<nsPtrHashKey<const nsINode>, bool> mAutofillInputs;
|
||||
|
||||
uint32_t mTimeout;
|
||||
uint32_t mMinResultsForPopup;
|
||||
|
|
|
@ -43,4 +43,12 @@ interface nsIFormFillController : nsISupports
|
|||
* @param aInput - The HTML <input> element to tag
|
||||
*/
|
||||
void markAsLoginManagerField(in nsIDOMHTMLInputElement aInput);
|
||||
|
||||
/*
|
||||
* Mark the specified <input> element as being managed by a form autofill component.
|
||||
* Autocomplete requests will be handed off to the autofill component.
|
||||
*
|
||||
* @param aInput - The HTML <input> element to mark
|
||||
*/
|
||||
void markAsAutofillField(in nsIDOMHTMLInputElement aInput);
|
||||
};
|
||||
|
|
|
@ -3071,6 +3071,23 @@ this.AddonManagerPrivate = {
|
|||
getUpgradeListener: function(aId) {
|
||||
return AddonManagerInternal.upgradeListeners.get(aId);
|
||||
},
|
||||
|
||||
/**
|
||||
* Predicate that returns true if we think the given extension ID
|
||||
* might have been generated by XPIProvider.
|
||||
*/
|
||||
isTemporaryInstallID: function(extensionId) {
|
||||
if (!gStarted)
|
||||
throw Components.Exception("AddonManager is not initialized",
|
||||
Cr.NS_ERROR_NOT_INITIALIZED);
|
||||
|
||||
if (!extensionId || typeof extensionId != "string")
|
||||
throw Components.Exception("extensionId must be a string",
|
||||
Cr.NS_ERROR_INVALID_ARG);
|
||||
|
||||
return AddonManagerInternal._getProviderByName("XPIProvider")
|
||||
.isTemporaryInstallID(extensionId);
|
||||
},
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
|
@ -1364,6 +1364,8 @@ function defineSyncGUID(aAddon) {
|
|||
});
|
||||
}
|
||||
|
||||
const TEMPORARY_ADDON_SUFFIX = "@temporary-addon";
|
||||
|
||||
// Generate a unique ID based on the path to this temporary add-on location.
|
||||
function generateTemporaryInstallID(aFile) {
|
||||
const hasher = Cc["@mozilla.org/security/hash;1"]
|
||||
|
@ -1374,7 +1376,7 @@ function generateTemporaryInstallID(aFile) {
|
|||
const sess = TEMP_INSTALL_ID_GEN_SESSION;
|
||||
hasher.update(sess, sess.length);
|
||||
hasher.update(data, data.length);
|
||||
let id = `${getHashStringForCrypto(hasher)}@temporary-addon`;
|
||||
let id = `${getHashStringForCrypto(hasher)}${TEMPORARY_ADDON_SUFFIX}`;
|
||||
logger.info(`Generated temp id ${id} (${sess.join("")}) for ${aFile.path}`);
|
||||
return id;
|
||||
}
|
||||
|
@ -3968,6 +3970,11 @@ this.XPIProvider = {
|
|||
return true;
|
||||
},
|
||||
|
||||
// Identify temporary install IDs.
|
||||
isTemporaryInstallID: function(id) {
|
||||
return id.endsWith(TEMPORARY_ADDON_SUFFIX);
|
||||
},
|
||||
|
||||
/**
|
||||
* Called to get an AddonInstall to download and install an add-on from a URL.
|
||||
*
|
||||
|
|