mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-12-02 01:48:05 +00:00
Merge inbound to mozilla-central r=merge a=merge
This commit is contained in:
commit
230207fe30
@ -1,7 +1,7 @@
|
|||||||
This is the debugger.html project output.
|
This is the debugger.html project output.
|
||||||
See https://github.com/devtools-html/debugger.html
|
See https://github.com/devtools-html/debugger.html
|
||||||
|
|
||||||
Taken from upstream commit: be179268c9b89390c13bdc9c4cca6000f6f583e5
|
Taken from upstream commit: 09d6d4f93135367b2639d78ad884434f73ab449c
|
||||||
|
|
||||||
Packages:
|
Packages:
|
||||||
- babel-plugin-transform-es2015-modules-commonjs @6.26.0
|
- babel-plugin-transform-es2015-modules-commonjs @6.26.0
|
||||||
|
@ -3739,5 +3739,3 @@ html[dir="rtl"] .dropdown {
|
|||||||
.theme-dark .result-list {
|
.theme-dark .result-list {
|
||||||
background-color: var(--theme-body-background);
|
background-color: var(--theme-body-background);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*# sourceMappingURL=debugger.css.map*/
|
|
@ -17612,7 +17612,7 @@ const {
|
|||||||
isOriginalId
|
isOriginalId
|
||||||
} = __webpack_require__(1389);
|
} = __webpack_require__(1389);
|
||||||
|
|
||||||
const { workerUtils: { WorkerDispatcher } } = __webpack_require__(1363);
|
const { workerUtils: { WorkerDispatcher } } = __webpack_require__(1390);
|
||||||
|
|
||||||
const dispatcher = new WorkerDispatcher();
|
const dispatcher = new WorkerDispatcher();
|
||||||
|
|
||||||
@ -18277,9 +18277,9 @@ WorkerDispatcher.prototype = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!this.worker) {
|
if (!this.worker) {
|
||||||
reject("Oops, The worker has shutdown!");
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.worker.removeEventListener("message", listener);
|
this.worker.removeEventListener("message", listener);
|
||||||
if (result.error) {
|
if (result.error) {
|
||||||
reject(result.error);
|
reject(result.error);
|
||||||
@ -20810,9 +20810,181 @@ module.exports = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
/***/ }),
|
/***/ }),
|
||||||
/* 1390 */,
|
/* 1390 */
|
||||||
/* 1391 */,
|
/***/ (function(module, exports, __webpack_require__) {
|
||||||
/* 1392 */,
|
|
||||||
|
/* 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/. */
|
||||||
|
|
||||||
|
const networkRequest = __webpack_require__(1391);
|
||||||
|
const workerUtils = __webpack_require__(1392);
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
networkRequest,
|
||||||
|
workerUtils
|
||||||
|
};
|
||||||
|
|
||||||
|
/***/ }),
|
||||||
|
/* 1391 */
|
||||||
|
/***/ (function(module, exports) {
|
||||||
|
|
||||||
|
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
|
function networkRequest(url, opts) {
|
||||||
|
return fetch(url, {
|
||||||
|
cache: opts.loadFromCache ? "default" : "no-cache"
|
||||||
|
}).then(res => {
|
||||||
|
if (res.status >= 200 && res.status < 300) {
|
||||||
|
return res.text().then(text => ({ content: text }));
|
||||||
|
}
|
||||||
|
return Promise.reject(`request failed with status ${res.status}`);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = networkRequest;
|
||||||
|
|
||||||
|
/***/ }),
|
||||||
|
/* 1392 */
|
||||||
|
/***/ (function(module, exports) {
|
||||||
|
|
||||||
|
function _asyncToGenerator(fn) { return function () { var gen = fn.apply(this, arguments); return new Promise(function (resolve, reject) { function step(key, arg) { try { var info = gen[key](arg); var value = info.value; } catch (error) { reject(error); return; } if (info.done) { resolve(value); } else { return Promise.resolve(value).then(function (value) { step("next", value); }, function (err) { step("throw", err); }); } } return step("next"); }); }; }
|
||||||
|
|
||||||
|
function WorkerDispatcher() {
|
||||||
|
this.msgId = 1;
|
||||||
|
this.worker = null;
|
||||||
|
} /* 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/. */
|
||||||
|
|
||||||
|
WorkerDispatcher.prototype = {
|
||||||
|
start(url) {
|
||||||
|
this.worker = new Worker(url);
|
||||||
|
this.worker.onerror = () => {
|
||||||
|
console.error(`Error in worker ${url}`);
|
||||||
|
};
|
||||||
|
},
|
||||||
|
|
||||||
|
stop() {
|
||||||
|
if (!this.worker) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.worker.terminate();
|
||||||
|
this.worker = null;
|
||||||
|
},
|
||||||
|
|
||||||
|
task(method) {
|
||||||
|
return (...args) => {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
const id = this.msgId++;
|
||||||
|
this.worker.postMessage({ id, method, args });
|
||||||
|
|
||||||
|
const listener = ({ data: result }) => {
|
||||||
|
if (result.id !== id) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!this.worker) {
|
||||||
|
reject("Oops, The worker has shutdown!");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.worker.removeEventListener("message", listener);
|
||||||
|
if (result.error) {
|
||||||
|
reject(result.error);
|
||||||
|
} else {
|
||||||
|
resolve(result.response);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
this.worker.addEventListener("message", listener);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
function workerHandler(publicInterface) {
|
||||||
|
return function (msg) {
|
||||||
|
const { id, method, args } = msg.data;
|
||||||
|
try {
|
||||||
|
const response = publicInterface[method].apply(undefined, args);
|
||||||
|
if (response instanceof Promise) {
|
||||||
|
response.then(val => self.postMessage({ id, response: val }),
|
||||||
|
// Error can't be sent via postMessage, so be sure to
|
||||||
|
// convert to string.
|
||||||
|
err => self.postMessage({ id, error: err.toString() }));
|
||||||
|
} else {
|
||||||
|
self.postMessage({ id, response });
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
// Error can't be sent via postMessage, so be sure to convert to
|
||||||
|
// string.
|
||||||
|
self.postMessage({ id, error: error.toString() });
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function streamingWorkerHandler(publicInterface, { timeout = 100 } = {}, worker = self) {
|
||||||
|
let streamingWorker = (() => {
|
||||||
|
var _ref = _asyncToGenerator(function* (id, tasks) {
|
||||||
|
let isWorking = true;
|
||||||
|
|
||||||
|
const intervalId = setTimeout(function () {
|
||||||
|
isWorking = false;
|
||||||
|
}, timeout);
|
||||||
|
|
||||||
|
const results = [];
|
||||||
|
while (tasks.length !== 0 && isWorking) {
|
||||||
|
const { callback, context, args } = tasks.shift();
|
||||||
|
const result = yield callback.call(context, args);
|
||||||
|
results.push(result);
|
||||||
|
}
|
||||||
|
worker.postMessage({ id, status: "pending", data: results });
|
||||||
|
clearInterval(intervalId);
|
||||||
|
|
||||||
|
if (tasks.length !== 0) {
|
||||||
|
yield streamingWorker(id, tasks);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return function streamingWorker(_x, _x2) {
|
||||||
|
return _ref.apply(this, arguments);
|
||||||
|
};
|
||||||
|
})();
|
||||||
|
|
||||||
|
return (() => {
|
||||||
|
var _ref2 = _asyncToGenerator(function* (msg) {
|
||||||
|
const { id, method, args } = msg.data;
|
||||||
|
const workerMethod = publicInterface[method];
|
||||||
|
if (!workerMethod) {
|
||||||
|
console.error(`Could not find ${method} defined in worker.`);
|
||||||
|
}
|
||||||
|
worker.postMessage({ id, status: "start" });
|
||||||
|
|
||||||
|
try {
|
||||||
|
const tasks = workerMethod(args);
|
||||||
|
yield streamingWorker(id, tasks);
|
||||||
|
worker.postMessage({ id, status: "done" });
|
||||||
|
} catch (error) {
|
||||||
|
worker.postMessage({ id, status: "error", error });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return function (_x3) {
|
||||||
|
return _ref2.apply(this, arguments);
|
||||||
|
};
|
||||||
|
})();
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
WorkerDispatcher,
|
||||||
|
workerHandler,
|
||||||
|
streamingWorkerHandler
|
||||||
|
};
|
||||||
|
|
||||||
|
/***/ }),
|
||||||
/* 1393 */
|
/* 1393 */
|
||||||
/***/ (function(module, exports, __webpack_require__) {
|
/***/ (function(module, exports, __webpack_require__) {
|
||||||
|
|
||||||
@ -21820,6 +21992,9 @@ function setSymbols(sourceId) {
|
|||||||
source,
|
source,
|
||||||
symbols
|
symbols
|
||||||
});
|
});
|
||||||
|
|
||||||
|
dispatch(setEmptyLines(source.id));
|
||||||
|
dispatch(setSourceMetaData(source.id));
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -24782,9 +24957,7 @@ function loadSourceText(source) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
await (0, _parser.setSource)(newSource);
|
await (0, _parser.setSource)(newSource);
|
||||||
await dispatch((0, _ast.setSymbols)(source.id));
|
dispatch((0, _ast.setSymbols)(source.id));
|
||||||
await dispatch((0, _ast.setEmptyLines)(source.id));
|
|
||||||
await dispatch((0, _ast.setSourceMetaData)(source.id));
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -45767,5 +45940,4 @@ function timing(store) {
|
|||||||
|
|
||||||
/***/ })
|
/***/ })
|
||||||
/******/ ]);
|
/******/ ]);
|
||||||
});
|
});
|
||||||
//# sourceMappingURL=debugger.js.map
|
|
@ -35432,9 +35432,9 @@ WorkerDispatcher.prototype = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!this.worker) {
|
if (!this.worker) {
|
||||||
reject("Oops, The worker has shutdown!");
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.worker.removeEventListener("message", listener);
|
this.worker.removeEventListener("message", listener);
|
||||||
if (result.error) {
|
if (result.error) {
|
||||||
reject(result.error);
|
reject(result.error);
|
||||||
@ -41824,5 +41824,4 @@ function extendsComponent(classes) {
|
|||||||
|
|
||||||
/***/ })
|
/***/ })
|
||||||
/******/ ]);
|
/******/ ]);
|
||||||
});
|
});
|
||||||
//# sourceMappingURL=parser-worker.js.map
|
|
@ -163,9 +163,9 @@ WorkerDispatcher.prototype = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!this.worker) {
|
if (!this.worker) {
|
||||||
reject("Oops, The worker has shutdown!");
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.worker.removeEventListener("message", listener);
|
this.worker.removeEventListener("message", listener);
|
||||||
if (result.error) {
|
if (result.error) {
|
||||||
reject(result.error);
|
reject(result.error);
|
||||||
@ -7591,5 +7591,4 @@ exports.SourceNode = SourceNode;
|
|||||||
/***/ })
|
/***/ })
|
||||||
|
|
||||||
/******/ });
|
/******/ });
|
||||||
});
|
});
|
||||||
//# sourceMappingURL=pretty-print-worker.js.map
|
|
@ -746,7 +746,7 @@ const {
|
|||||||
isOriginalId
|
isOriginalId
|
||||||
} = __webpack_require__(1389);
|
} = __webpack_require__(1389);
|
||||||
|
|
||||||
const { workerUtils: { WorkerDispatcher } } = __webpack_require__(1363);
|
const { workerUtils: { WorkerDispatcher } } = __webpack_require__(1390);
|
||||||
|
|
||||||
const dispatcher = new WorkerDispatcher();
|
const dispatcher = new WorkerDispatcher();
|
||||||
|
|
||||||
@ -945,9 +945,9 @@ WorkerDispatcher.prototype = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!this.worker) {
|
if (!this.worker) {
|
||||||
reject("Oops, The worker has shutdown!");
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.worker.removeEventListener("message", listener);
|
this.worker.removeEventListener("message", listener);
|
||||||
if (result.error) {
|
if (result.error) {
|
||||||
reject(result.error);
|
reject(result.error);
|
||||||
@ -1124,6 +1124,184 @@ module.exports = {
|
|||||||
|
|
||||||
/***/ }),
|
/***/ }),
|
||||||
|
|
||||||
|
/***/ 1390:
|
||||||
|
/***/ (function(module, exports, __webpack_require__) {
|
||||||
|
|
||||||
|
/* 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/. */
|
||||||
|
|
||||||
|
const networkRequest = __webpack_require__(1391);
|
||||||
|
const workerUtils = __webpack_require__(1392);
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
networkRequest,
|
||||||
|
workerUtils
|
||||||
|
};
|
||||||
|
|
||||||
|
/***/ }),
|
||||||
|
|
||||||
|
/***/ 1391:
|
||||||
|
/***/ (function(module, exports) {
|
||||||
|
|
||||||
|
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
|
function networkRequest(url, opts) {
|
||||||
|
return fetch(url, {
|
||||||
|
cache: opts.loadFromCache ? "default" : "no-cache"
|
||||||
|
}).then(res => {
|
||||||
|
if (res.status >= 200 && res.status < 300) {
|
||||||
|
return res.text().then(text => ({ content: text }));
|
||||||
|
}
|
||||||
|
return Promise.reject(`request failed with status ${res.status}`);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = networkRequest;
|
||||||
|
|
||||||
|
/***/ }),
|
||||||
|
|
||||||
|
/***/ 1392:
|
||||||
|
/***/ (function(module, exports) {
|
||||||
|
|
||||||
|
function _asyncToGenerator(fn) { return function () { var gen = fn.apply(this, arguments); return new Promise(function (resolve, reject) { function step(key, arg) { try { var info = gen[key](arg); var value = info.value; } catch (error) { reject(error); return; } if (info.done) { resolve(value); } else { return Promise.resolve(value).then(function (value) { step("next", value); }, function (err) { step("throw", err); }); } } return step("next"); }); }; }
|
||||||
|
|
||||||
|
function WorkerDispatcher() {
|
||||||
|
this.msgId = 1;
|
||||||
|
this.worker = null;
|
||||||
|
} /* 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/. */
|
||||||
|
|
||||||
|
WorkerDispatcher.prototype = {
|
||||||
|
start(url) {
|
||||||
|
this.worker = new Worker(url);
|
||||||
|
this.worker.onerror = () => {
|
||||||
|
console.error(`Error in worker ${url}`);
|
||||||
|
};
|
||||||
|
},
|
||||||
|
|
||||||
|
stop() {
|
||||||
|
if (!this.worker) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.worker.terminate();
|
||||||
|
this.worker = null;
|
||||||
|
},
|
||||||
|
|
||||||
|
task(method) {
|
||||||
|
return (...args) => {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
const id = this.msgId++;
|
||||||
|
this.worker.postMessage({ id, method, args });
|
||||||
|
|
||||||
|
const listener = ({ data: result }) => {
|
||||||
|
if (result.id !== id) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!this.worker) {
|
||||||
|
reject("Oops, The worker has shutdown!");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.worker.removeEventListener("message", listener);
|
||||||
|
if (result.error) {
|
||||||
|
reject(result.error);
|
||||||
|
} else {
|
||||||
|
resolve(result.response);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
this.worker.addEventListener("message", listener);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
function workerHandler(publicInterface) {
|
||||||
|
return function (msg) {
|
||||||
|
const { id, method, args } = msg.data;
|
||||||
|
try {
|
||||||
|
const response = publicInterface[method].apply(undefined, args);
|
||||||
|
if (response instanceof Promise) {
|
||||||
|
response.then(val => self.postMessage({ id, response: val }),
|
||||||
|
// Error can't be sent via postMessage, so be sure to
|
||||||
|
// convert to string.
|
||||||
|
err => self.postMessage({ id, error: err.toString() }));
|
||||||
|
} else {
|
||||||
|
self.postMessage({ id, response });
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
// Error can't be sent via postMessage, so be sure to convert to
|
||||||
|
// string.
|
||||||
|
self.postMessage({ id, error: error.toString() });
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function streamingWorkerHandler(publicInterface, { timeout = 100 } = {}, worker = self) {
|
||||||
|
let streamingWorker = (() => {
|
||||||
|
var _ref = _asyncToGenerator(function* (id, tasks) {
|
||||||
|
let isWorking = true;
|
||||||
|
|
||||||
|
const intervalId = setTimeout(function () {
|
||||||
|
isWorking = false;
|
||||||
|
}, timeout);
|
||||||
|
|
||||||
|
const results = [];
|
||||||
|
while (tasks.length !== 0 && isWorking) {
|
||||||
|
const { callback, context, args } = tasks.shift();
|
||||||
|
const result = yield callback.call(context, args);
|
||||||
|
results.push(result);
|
||||||
|
}
|
||||||
|
worker.postMessage({ id, status: "pending", data: results });
|
||||||
|
clearInterval(intervalId);
|
||||||
|
|
||||||
|
if (tasks.length !== 0) {
|
||||||
|
yield streamingWorker(id, tasks);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return function streamingWorker(_x, _x2) {
|
||||||
|
return _ref.apply(this, arguments);
|
||||||
|
};
|
||||||
|
})();
|
||||||
|
|
||||||
|
return (() => {
|
||||||
|
var _ref2 = _asyncToGenerator(function* (msg) {
|
||||||
|
const { id, method, args } = msg.data;
|
||||||
|
const workerMethod = publicInterface[method];
|
||||||
|
if (!workerMethod) {
|
||||||
|
console.error(`Could not find ${method} defined in worker.`);
|
||||||
|
}
|
||||||
|
worker.postMessage({ id, status: "start" });
|
||||||
|
|
||||||
|
try {
|
||||||
|
const tasks = workerMethod(args);
|
||||||
|
yield streamingWorker(id, tasks);
|
||||||
|
worker.postMessage({ id, status: "done" });
|
||||||
|
} catch (error) {
|
||||||
|
worker.postMessage({ id, status: "error", error });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return function (_x3) {
|
||||||
|
return _ref2.apply(this, arguments);
|
||||||
|
};
|
||||||
|
})();
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
WorkerDispatcher,
|
||||||
|
workerHandler,
|
||||||
|
streamingWorkerHandler
|
||||||
|
};
|
||||||
|
|
||||||
|
/***/ }),
|
||||||
|
|
||||||
/***/ 1393:
|
/***/ 1393:
|
||||||
/***/ (function(module, exports, __webpack_require__) {
|
/***/ (function(module, exports, __webpack_require__) {
|
||||||
|
|
||||||
@ -3293,5 +3471,4 @@ module.exports = freeGlobal;
|
|||||||
/***/ })
|
/***/ })
|
||||||
|
|
||||||
/******/ });
|
/******/ });
|
||||||
});
|
});
|
||||||
//# sourceMappingURL=search-worker.js.map
|
|
@ -101,6 +101,19 @@ void* get_proc_address_from_glcontext(void* glcontext_ptr, const char* procname)
|
|||||||
return reinterpret_cast<void*>(p);
|
return reinterpret_cast<void*>(p);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
gecko_profiler_register_thread(const char* name)
|
||||||
|
{
|
||||||
|
char stackTop;
|
||||||
|
profiler_register_thread(name, &stackTop);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
gecko_profiler_unregister_thread()
|
||||||
|
{
|
||||||
|
profiler_unregister_thread();
|
||||||
|
}
|
||||||
|
|
||||||
namespace mozilla {
|
namespace mozilla {
|
||||||
namespace layers {
|
namespace layers {
|
||||||
|
|
||||||
|
@ -244,6 +244,8 @@ gfxCoreTextShaper::SetGlyphsFromRun(gfxShapedText *aShapedText,
|
|||||||
uint32_t aLength,
|
uint32_t aLength,
|
||||||
CTRunRef aCTRun)
|
CTRunRef aCTRun)
|
||||||
{
|
{
|
||||||
|
typedef gfxShapedText::CompressedGlyph CompressedGlyph;
|
||||||
|
|
||||||
int32_t direction = aShapedText->IsRightToLeft() ? -1 : 1;
|
int32_t direction = aShapedText->IsRightToLeft() ? -1 : 1;
|
||||||
|
|
||||||
int32_t numGlyphs = ::CTRunGetGlyphCount(aCTRun);
|
int32_t numGlyphs = ::CTRunGetGlyphCount(aCTRun);
|
||||||
@ -319,8 +321,7 @@ gfxCoreTextShaper::SetGlyphsFromRun(gfxShapedText *aShapedText,
|
|||||||
nullptr, nullptr, nullptr);
|
nullptr, nullptr, nullptr);
|
||||||
|
|
||||||
AutoTArray<gfxShapedText::DetailedGlyph,1> detailedGlyphs;
|
AutoTArray<gfxShapedText::DetailedGlyph,1> detailedGlyphs;
|
||||||
gfxShapedText::CompressedGlyph *charGlyphs =
|
CompressedGlyph* charGlyphs = aShapedText->GetCharacterGlyphs() + aOffset;
|
||||||
aShapedText->GetCharacterGlyphs() + aOffset;
|
|
||||||
|
|
||||||
// CoreText gives us the glyphindex-to-charindex mapping, which relates each glyph
|
// CoreText gives us the glyphindex-to-charindex mapping, which relates each glyph
|
||||||
// to a source text character; we also need the charindex-to-glyphindex mapping to
|
// to a source text character; we also need the charindex-to-glyphindex mapping to
|
||||||
@ -540,10 +541,10 @@ gfxCoreTextShaper::SetGlyphsFromRun(gfxShapedText *aShapedText,
|
|||||||
advance = int32_t(toNextGlyph * appUnitsPerDevUnit);
|
advance = int32_t(toNextGlyph * appUnitsPerDevUnit);
|
||||||
}
|
}
|
||||||
|
|
||||||
gfxTextRun::CompressedGlyph textRunGlyph;
|
bool isClusterStart = charGlyphs[baseCharIndex].IsClusterStart();
|
||||||
textRunGlyph.SetComplex(charGlyphs[baseCharIndex].IsClusterStart(),
|
aShapedText->SetGlyphs(aOffset + baseCharIndex,
|
||||||
true, detailedGlyphs.Length());
|
CompressedGlyph::MakeComplex(isClusterStart, true,
|
||||||
aShapedText->SetGlyphs(aOffset + baseCharIndex, textRunGlyph,
|
detailedGlyphs.Length()),
|
||||||
detailedGlyphs.Elements());
|
detailedGlyphs.Elements());
|
||||||
|
|
||||||
detailedGlyphs.Clear();
|
detailedGlyphs.Clear();
|
||||||
@ -551,7 +552,7 @@ gfxCoreTextShaper::SetGlyphsFromRun(gfxShapedText *aShapedText,
|
|||||||
|
|
||||||
// the rest of the chars in the group are ligature continuations, no associated glyphs
|
// the rest of the chars in the group are ligature continuations, no associated glyphs
|
||||||
while (++baseCharIndex != endCharIndex && baseCharIndex < wordLength) {
|
while (++baseCharIndex != endCharIndex && baseCharIndex < wordLength) {
|
||||||
gfxShapedText::CompressedGlyph &shapedTextGlyph = charGlyphs[baseCharIndex];
|
CompressedGlyph &shapedTextGlyph = charGlyphs[baseCharIndex];
|
||||||
NS_ASSERTION(!shapedTextGlyph.IsSimpleGlyph(), "overwriting a simple glyph");
|
NS_ASSERTION(!shapedTextGlyph.IsSimpleGlyph(), "overwriting a simple glyph");
|
||||||
shapedTextGlyph.SetComplex(inOrder && shapedTextGlyph.IsClusterStart(), false, 0);
|
shapedTextGlyph.SetComplex(inOrder && shapedTextGlyph.IsClusterStart(), false, 0);
|
||||||
}
|
}
|
||||||
|
@ -66,13 +66,14 @@ void
|
|||||||
gfxFT2Font::AddRange(const char16_t *aText, uint32_t aOffset,
|
gfxFT2Font::AddRange(const char16_t *aText, uint32_t aOffset,
|
||||||
uint32_t aLength, gfxShapedText *aShapedText)
|
uint32_t aLength, gfxShapedText *aShapedText)
|
||||||
{
|
{
|
||||||
|
typedef gfxShapedText::CompressedGlyph CompressedGlyph;
|
||||||
|
|
||||||
const uint32_t appUnitsPerDevUnit = aShapedText->GetAppUnitsPerDevUnit();
|
const uint32_t appUnitsPerDevUnit = aShapedText->GetAppUnitsPerDevUnit();
|
||||||
// we'll pass this in/figure it out dynamically, but at this point there can be only one face.
|
// we'll pass this in/figure it out dynamically, but at this point there can be only one face.
|
||||||
gfxFT2LockedFace faceLock(this);
|
gfxFT2LockedFace faceLock(this);
|
||||||
FT_Face face = faceLock.get();
|
FT_Face face = faceLock.get();
|
||||||
|
|
||||||
gfxShapedText::CompressedGlyph *charGlyphs =
|
CompressedGlyph* charGlyphs = aShapedText->GetCharacterGlyphs();
|
||||||
aShapedText->GetCharacterGlyphs();
|
|
||||||
|
|
||||||
const gfxFT2Font::CachedGlyphData *cgd = nullptr, *cgdNext = nullptr;
|
const gfxFT2Font::CachedGlyphData *cgd = nullptr, *cgdNext = nullptr;
|
||||||
|
|
||||||
@ -139,8 +140,8 @@ gfxFT2Font::AddRange(const char16_t *aText, uint32_t aOffset,
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (advance >= 0 &&
|
if (advance >= 0 &&
|
||||||
gfxShapedText::CompressedGlyph::IsSimpleAdvance(advance) &&
|
CompressedGlyph::IsSimpleAdvance(advance) &&
|
||||||
gfxShapedText::CompressedGlyph::IsSimpleGlyphID(gid)) {
|
CompressedGlyph::IsSimpleGlyphID(gid)) {
|
||||||
charGlyphs[aOffset].SetSimpleGlyph(advance, gid);
|
charGlyphs[aOffset].SetSimpleGlyph(advance, gid);
|
||||||
} else if (gid == 0) {
|
} else if (gid == 0) {
|
||||||
// gid = 0 only happens when the glyph is missing from the font
|
// gid = 0 only happens when the glyph is missing from the font
|
||||||
@ -151,9 +152,11 @@ gfxFT2Font::AddRange(const char16_t *aText, uint32_t aOffset,
|
|||||||
NS_ASSERTION(details.mGlyphID == gid,
|
NS_ASSERTION(details.mGlyphID == gid,
|
||||||
"Seriously weird glyph ID detected!");
|
"Seriously weird glyph ID detected!");
|
||||||
details.mAdvance = advance;
|
details.mAdvance = advance;
|
||||||
gfxShapedText::CompressedGlyph g;
|
bool isClusterStart = charGlyphs[aOffset].IsClusterStart();
|
||||||
g.SetComplex(charGlyphs[aOffset].IsClusterStart(), true, 1);
|
aShapedText->SetGlyphs(aOffset,
|
||||||
aShapedText->SetGlyphs(aOffset, g, &details);
|
CompressedGlyph::MakeComplex(isClusterStart,
|
||||||
|
true, 1),
|
||||||
|
&details);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -571,10 +571,10 @@ gfxShapedText::SetupClusterBoundaries(uint32_t aOffset,
|
|||||||
const char16_t *aString,
|
const char16_t *aString,
|
||||||
uint32_t aLength)
|
uint32_t aLength)
|
||||||
{
|
{
|
||||||
CompressedGlyph *glyphs = GetCharacterGlyphs() + aOffset;
|
CompressedGlyph* glyphs = GetCharacterGlyphs() + aOffset;
|
||||||
|
|
||||||
gfxTextRun::CompressedGlyph extendCluster;
|
CompressedGlyph extendCluster =
|
||||||
extendCluster.SetComplex(false, true, 0);
|
CompressedGlyph::MakeComplex(false, true, 0);
|
||||||
|
|
||||||
ClusterIterator iter(aString, aLength);
|
ClusterIterator iter(aString, aLength);
|
||||||
|
|
||||||
|
@ -763,8 +763,6 @@ public:
|
|||||||
*/
|
*/
|
||||||
class CompressedGlyph {
|
class CompressedGlyph {
|
||||||
public:
|
public:
|
||||||
CompressedGlyph() { mValue = 0; }
|
|
||||||
|
|
||||||
enum {
|
enum {
|
||||||
// Indicates that a cluster and ligature group starts at this
|
// Indicates that a cluster and ligature group starts at this
|
||||||
// character; this character has a single glyph with a reasonable
|
// character; this character has a single glyph with a reasonable
|
||||||
@ -894,25 +892,52 @@ public:
|
|||||||
return toggle;
|
return toggle;
|
||||||
}
|
}
|
||||||
|
|
||||||
CompressedGlyph& SetSimpleGlyph(uint32_t aAdvanceAppUnits, uint32_t aGlyph) {
|
// Create a CompressedGlyph value representing a simple glyph with
|
||||||
|
// no extra flags (line-break or is_space) set.
|
||||||
|
static CompressedGlyph
|
||||||
|
MakeSimpleGlyph(uint32_t aAdvanceAppUnits, uint32_t aGlyph) {
|
||||||
NS_ASSERTION(IsSimpleAdvance(aAdvanceAppUnits), "Advance overflow");
|
NS_ASSERTION(IsSimpleAdvance(aAdvanceAppUnits), "Advance overflow");
|
||||||
NS_ASSERTION(IsSimpleGlyphID(aGlyph), "Glyph overflow");
|
NS_ASSERTION(IsSimpleGlyphID(aGlyph), "Glyph overflow");
|
||||||
|
CompressedGlyph g;
|
||||||
|
g.mValue = FLAG_IS_SIMPLE_GLYPH |
|
||||||
|
(aAdvanceAppUnits << ADVANCE_SHIFT) |
|
||||||
|
aGlyph;
|
||||||
|
return g;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Assign a simple glyph value to an existing CompressedGlyph record,
|
||||||
|
// preserving line-break/is-space flags if present.
|
||||||
|
CompressedGlyph& SetSimpleGlyph(uint32_t aAdvanceAppUnits,
|
||||||
|
uint32_t aGlyph) {
|
||||||
NS_ASSERTION(!CharTypeFlags(), "Char type flags lost");
|
NS_ASSERTION(!CharTypeFlags(), "Char type flags lost");
|
||||||
mValue = (mValue & (FLAGS_CAN_BREAK_BEFORE | FLAG_CHAR_IS_SPACE)) |
|
mValue = (mValue & (FLAGS_CAN_BREAK_BEFORE | FLAG_CHAR_IS_SPACE)) |
|
||||||
FLAG_IS_SIMPLE_GLYPH |
|
MakeSimpleGlyph(aAdvanceAppUnits, aGlyph).mValue;
|
||||||
(aAdvanceAppUnits << ADVANCE_SHIFT) | aGlyph;
|
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Create a CompressedGlyph value representing a complex glyph record,
|
||||||
|
// without any line-break or char-type flags.
|
||||||
|
static CompressedGlyph
|
||||||
|
MakeComplex(bool aClusterStart, bool aLigatureStart,
|
||||||
|
uint32_t aGlyphCount) {
|
||||||
|
CompressedGlyph g;
|
||||||
|
g.mValue = FLAG_NOT_MISSING |
|
||||||
|
(aClusterStart ? 0 : FLAG_NOT_CLUSTER_START) |
|
||||||
|
(aLigatureStart ? 0 : FLAG_NOT_LIGATURE_GROUP_START) |
|
||||||
|
(aGlyphCount << GLYPH_COUNT_SHIFT);
|
||||||
|
return g;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Assign a complex glyph value to an existing CompressedGlyph record,
|
||||||
|
// preserving line-break/char-type flags if present.
|
||||||
CompressedGlyph& SetComplex(bool aClusterStart, bool aLigatureStart,
|
CompressedGlyph& SetComplex(bool aClusterStart, bool aLigatureStart,
|
||||||
uint32_t aGlyphCount) {
|
uint32_t aGlyphCount) {
|
||||||
mValue = (mValue & (FLAGS_CAN_BREAK_BEFORE | FLAG_CHAR_IS_SPACE)) |
|
mValue = (mValue & (FLAGS_CAN_BREAK_BEFORE | FLAG_CHAR_IS_SPACE)) |
|
||||||
FLAG_NOT_MISSING |
|
|
||||||
CharTypeFlags() |
|
CharTypeFlags() |
|
||||||
(aClusterStart ? 0 : FLAG_NOT_CLUSTER_START) |
|
MakeComplex(aClusterStart, aLigatureStart, aGlyphCount).mValue;
|
||||||
(aLigatureStart ? 0 : FLAG_NOT_LIGATURE_GROUP_START) |
|
|
||||||
(aGlyphCount << GLYPH_COUNT_SHIFT);
|
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Missing glyphs are treated as ligature group starts; don't mess with
|
* Missing glyphs are treated as ligature group starts; don't mess with
|
||||||
* the cluster-start flag (see bugs 618870 and 619286).
|
* the cluster-start flag (see bugs 618870 and 619286).
|
||||||
|
@ -216,6 +216,8 @@ gfxGraphiteShaper::SetGlyphsFromSegment(gfxShapedText *aShapedText,
|
|||||||
gr_segment *aSegment,
|
gr_segment *aSegment,
|
||||||
RoundingFlags aRounding)
|
RoundingFlags aRounding)
|
||||||
{
|
{
|
||||||
|
typedef gfxShapedText::CompressedGlyph CompressedGlyph;
|
||||||
|
|
||||||
int32_t dev2appUnits = aShapedText->GetAppUnitsPerDevUnit();
|
int32_t dev2appUnits = aShapedText->GetAppUnitsPerDevUnit();
|
||||||
bool rtl = aShapedText->IsRightToLeft();
|
bool rtl = aShapedText->IsRightToLeft();
|
||||||
|
|
||||||
@ -289,8 +291,7 @@ gfxGraphiteShaper::SetGlyphsFromSegment(gfxShapedText *aShapedText,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
gfxShapedText::CompressedGlyph *charGlyphs =
|
CompressedGlyph* charGlyphs = aShapedText->GetCharacterGlyphs() + aOffset;
|
||||||
aShapedText->GetCharacterGlyphs() + aOffset;
|
|
||||||
|
|
||||||
bool roundX = bool(aRounding & RoundingFlags::kRoundX);
|
bool roundX = bool(aRounding & RoundingFlags::kRoundX);
|
||||||
bool roundY = bool(aRounding & RoundingFlags::kRoundY);
|
bool roundY = bool(aRounding & RoundingFlags::kRoundY);
|
||||||
@ -326,8 +327,8 @@ gfxGraphiteShaper::SetGlyphsFromSegment(gfxShapedText *aShapedText,
|
|||||||
uint32_t appAdvance = roundX ? NSToIntRound(adv) * dev2appUnits
|
uint32_t appAdvance = roundX ? NSToIntRound(adv) * dev2appUnits
|
||||||
: NSToIntRound(adv * dev2appUnits);
|
: NSToIntRound(adv * dev2appUnits);
|
||||||
if (c.nGlyphs == 1 &&
|
if (c.nGlyphs == 1 &&
|
||||||
gfxShapedText::CompressedGlyph::IsSimpleGlyphID(gids[c.baseGlyph]) &&
|
CompressedGlyph::IsSimpleGlyphID(gids[c.baseGlyph]) &&
|
||||||
gfxShapedText::CompressedGlyph::IsSimpleAdvance(appAdvance) &&
|
CompressedGlyph::IsSimpleAdvance(appAdvance) &&
|
||||||
charGlyphs[offs].IsClusterStart() &&
|
charGlyphs[offs].IsClusterStart() &&
|
||||||
yLocs[c.baseGlyph] == 0)
|
yLocs[c.baseGlyph] == 0)
|
||||||
{
|
{
|
||||||
@ -352,15 +353,17 @@ gfxGraphiteShaper::SetGlyphsFromSegment(gfxShapedText *aShapedText,
|
|||||||
d->mAdvance = 0;
|
d->mAdvance = 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
gfxShapedText::CompressedGlyph g;
|
bool isClusterStart = charGlyphs[offs].IsClusterStart();
|
||||||
g.SetComplex(charGlyphs[offs].IsClusterStart(),
|
aShapedText->SetGlyphs(aOffset + offs,
|
||||||
true, details.Length());
|
CompressedGlyph::MakeComplex(isClusterStart,
|
||||||
aShapedText->SetGlyphs(aOffset + offs, g, details.Elements());
|
true,
|
||||||
|
details.Length()),
|
||||||
|
details.Elements());
|
||||||
}
|
}
|
||||||
|
|
||||||
for (uint32_t j = c.baseChar + 1; j < c.baseChar + c.nChars; ++j) {
|
for (uint32_t j = c.baseChar + 1; j < c.baseChar + c.nChars; ++j) {
|
||||||
NS_ASSERTION(j < aLength, "unexpected offset");
|
NS_ASSERTION(j < aLength, "unexpected offset");
|
||||||
gfxShapedText::CompressedGlyph &g = charGlyphs[j];
|
CompressedGlyph &g = charGlyphs[j];
|
||||||
NS_ASSERTION(!g.IsSimpleGlyph(), "overwriting a simple glyph");
|
NS_ASSERTION(!g.IsSimpleGlyph(), "overwriting a simple glyph");
|
||||||
g.SetComplex(g.IsClusterStart(), false, 0);
|
g.SetComplex(g.IsClusterStart(), false, 0);
|
||||||
}
|
}
|
||||||
|
@ -1529,6 +1529,8 @@ gfxHarfBuzzShaper::SetGlyphsFromRun(gfxShapedText *aShapedText,
|
|||||||
bool aVertical,
|
bool aVertical,
|
||||||
RoundingFlags aRounding)
|
RoundingFlags aRounding)
|
||||||
{
|
{
|
||||||
|
typedef gfxShapedText::CompressedGlyph CompressedGlyph;
|
||||||
|
|
||||||
uint32_t numGlyphs;
|
uint32_t numGlyphs;
|
||||||
const hb_glyph_info_t *ginfo = hb_buffer_get_glyph_infos(mBuffer, &numGlyphs);
|
const hb_glyph_info_t *ginfo = hb_buffer_get_glyph_infos(mBuffer, &numGlyphs);
|
||||||
if (numGlyphs == 0) {
|
if (numGlyphs == 0) {
|
||||||
@ -1569,8 +1571,7 @@ gfxHarfBuzzShaper::SetGlyphsFromRun(gfxShapedText *aShapedText,
|
|||||||
}
|
}
|
||||||
|
|
||||||
int32_t appUnitsPerDevUnit = aShapedText->GetAppUnitsPerDevUnit();
|
int32_t appUnitsPerDevUnit = aShapedText->GetAppUnitsPerDevUnit();
|
||||||
gfxShapedText::CompressedGlyph *charGlyphs =
|
CompressedGlyph* charGlyphs = aShapedText->GetCharacterGlyphs() + aOffset;
|
||||||
aShapedText->GetCharacterGlyphs() + aOffset;
|
|
||||||
|
|
||||||
// factor to convert 16.16 fixed-point pixels to app units
|
// factor to convert 16.16 fixed-point pixels to app units
|
||||||
// (only used if not rounding)
|
// (only used if not rounding)
|
||||||
@ -1718,8 +1719,8 @@ gfxHarfBuzzShaper::SetGlyphsFromRun(gfxShapedText *aShapedText,
|
|||||||
}
|
}
|
||||||
// Check if it's a simple one-to-one mapping
|
// Check if it's a simple one-to-one mapping
|
||||||
if (glyphsInClump == 1 &&
|
if (glyphsInClump == 1 &&
|
||||||
gfxTextRun::CompressedGlyph::IsSimpleGlyphID(ginfo[glyphStart].codepoint) &&
|
CompressedGlyph::IsSimpleGlyphID(ginfo[glyphStart].codepoint) &&
|
||||||
gfxTextRun::CompressedGlyph::IsSimpleAdvance(advance) &&
|
CompressedGlyph::IsSimpleAdvance(advance) &&
|
||||||
charGlyphs[baseCharIndex].IsClusterStart() &&
|
charGlyphs[baseCharIndex].IsClusterStart() &&
|
||||||
iOffset == 0 && b_offset == 0 &&
|
iOffset == 0 && b_offset == 0 &&
|
||||||
b_advance == 0 && bPos == 0)
|
b_advance == 0 && bPos == 0)
|
||||||
@ -1789,11 +1790,12 @@ gfxHarfBuzzShaper::SetGlyphsFromRun(gfxShapedText *aShapedText,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
gfxShapedText::CompressedGlyph g;
|
bool isClusterStart = charGlyphs[baseCharIndex].IsClusterStart();
|
||||||
g.SetComplex(charGlyphs[baseCharIndex].IsClusterStart(),
|
|
||||||
true, detailedGlyphs.Length());
|
|
||||||
aShapedText->SetGlyphs(aOffset + baseCharIndex,
|
aShapedText->SetGlyphs(aOffset + baseCharIndex,
|
||||||
g, detailedGlyphs.Elements());
|
CompressedGlyph::MakeComplex(isClusterStart,
|
||||||
|
true,
|
||||||
|
detailedGlyphs.Length()),
|
||||||
|
detailedGlyphs.Elements());
|
||||||
|
|
||||||
detailedGlyphs.Clear();
|
detailedGlyphs.Clear();
|
||||||
}
|
}
|
||||||
@ -1802,7 +1804,7 @@ gfxHarfBuzzShaper::SetGlyphsFromRun(gfxShapedText *aShapedText,
|
|||||||
// no associated glyphs
|
// no associated glyphs
|
||||||
while (++baseCharIndex != endCharIndex &&
|
while (++baseCharIndex != endCharIndex &&
|
||||||
baseCharIndex < int32_t(wordLength)) {
|
baseCharIndex < int32_t(wordLength)) {
|
||||||
gfxShapedText::CompressedGlyph &g = charGlyphs[baseCharIndex];
|
CompressedGlyph &g = charGlyphs[baseCharIndex];
|
||||||
NS_ASSERTION(!g.IsSimpleGlyph(), "overwriting a simple glyph");
|
NS_ASSERTION(!g.IsSimpleGlyph(), "overwriting a simple glyph");
|
||||||
g.SetComplex(g.IsClusterStart(), false, 0);
|
g.SetComplex(g.IsClusterStart(), false, 0);
|
||||||
}
|
}
|
||||||
|
@ -1658,8 +1658,8 @@ gfxTextRun::SetSpaceGlyphIfSimple(gfxFont* aFont, uint32_t aCharIndex,
|
|||||||
|
|
||||||
AddGlyphRun(aFont, gfxTextRange::kFontGroup, aCharIndex, false,
|
AddGlyphRun(aFont, gfxTextRange::kFontGroup, aCharIndex, false,
|
||||||
aOrientation);
|
aOrientation);
|
||||||
CompressedGlyph g;
|
CompressedGlyph g =
|
||||||
g.SetSimpleGlyph(spaceWidthAppUnits, spaceGlyph);
|
CompressedGlyph::MakeSimpleGlyph(spaceWidthAppUnits, spaceGlyph);
|
||||||
if (aSpaceChar == ' ') {
|
if (aSpaceChar == ' ') {
|
||||||
g.SetIsSpace();
|
g.SetIsSpace();
|
||||||
}
|
}
|
||||||
@ -2756,8 +2756,8 @@ gfxFontGroup::InitScriptRun(DrawTarget* aDrawTarget,
|
|||||||
gfxTextRun::DetailedGlyph detailedGlyph;
|
gfxTextRun::DetailedGlyph detailedGlyph;
|
||||||
detailedGlyph.mGlyphID = mainFont->GetSpaceGlyph();
|
detailedGlyph.mGlyphID = mainFont->GetSpaceGlyph();
|
||||||
detailedGlyph.mAdvance = advance;
|
detailedGlyph.mAdvance = advance;
|
||||||
gfxShapedText::CompressedGlyph g;
|
CompressedGlyph g =
|
||||||
g.SetComplex(true, true, 1);
|
CompressedGlyph::MakeComplex(true, true, 1);
|
||||||
aTextRun->SetGlyphs(aOffset + index,
|
aTextRun->SetGlyphs(aOffset + index,
|
||||||
g, &detailedGlyph);
|
g, &detailedGlyph);
|
||||||
}
|
}
|
||||||
|
@ -842,6 +842,7 @@ private:
|
|||||||
class gfxFontGroup final : public gfxTextRunFactory {
|
class gfxFontGroup final : public gfxTextRunFactory {
|
||||||
public:
|
public:
|
||||||
typedef mozilla::unicode::Script Script;
|
typedef mozilla::unicode::Script Script;
|
||||||
|
typedef gfxShapedText::CompressedGlyph CompressedGlyph;
|
||||||
|
|
||||||
static void Shutdown(); // platform must call this to release the languageAtomService
|
static void Shutdown(); // platform must call this to release the languageAtomService
|
||||||
|
|
||||||
|
@ -7,7 +7,7 @@ use std::os::raw::{c_void, c_char, c_float};
|
|||||||
use gleam::gl;
|
use gleam::gl;
|
||||||
|
|
||||||
use webrender_api::*;
|
use webrender_api::*;
|
||||||
use webrender::{ReadPixelsFormat, Renderer, RendererOptions};
|
use webrender::{ReadPixelsFormat, Renderer, RendererOptions, ThreadListener};
|
||||||
use webrender::{ExternalImage, ExternalImageHandler, ExternalImageSource};
|
use webrender::{ExternalImage, ExternalImageHandler, ExternalImageSource};
|
||||||
use webrender::DebugFlags;
|
use webrender::DebugFlags;
|
||||||
use webrender::{ApiRecordingReceiver, BinaryRecorder};
|
use webrender::{ApiRecordingReceiver, BinaryRecorder};
|
||||||
@ -587,14 +587,48 @@ pub unsafe extern "C" fn wr_rendered_epochs_delete(pipeline_epochs: *mut WrRende
|
|||||||
Box::from_raw(pipeline_epochs);
|
Box::from_raw(pipeline_epochs);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
extern "C" {
|
||||||
|
fn gecko_profiler_register_thread(name: *const ::std::os::raw::c_char);
|
||||||
|
fn gecko_profiler_unregister_thread();
|
||||||
|
}
|
||||||
|
|
||||||
|
struct GeckoProfilerThreadListener {}
|
||||||
|
|
||||||
|
impl GeckoProfilerThreadListener {
|
||||||
|
pub fn new() -> GeckoProfilerThreadListener {
|
||||||
|
GeckoProfilerThreadListener{}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ThreadListener for GeckoProfilerThreadListener {
|
||||||
|
fn thread_started(&self, thread_name: &str) {
|
||||||
|
let name = CString::new(thread_name).unwrap();
|
||||||
|
unsafe {
|
||||||
|
// gecko_profiler_register_thread copies the passed name here.
|
||||||
|
gecko_profiler_register_thread(name.as_ptr());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn thread_stopped(&self, _: &str) {
|
||||||
|
unsafe {
|
||||||
|
gecko_profiler_unregister_thread();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub struct WrThreadPool(Arc<rayon::ThreadPool>);
|
pub struct WrThreadPool(Arc<rayon::ThreadPool>);
|
||||||
|
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
pub unsafe extern "C" fn wr_thread_pool_new() -> *mut WrThreadPool {
|
pub unsafe extern "C" fn wr_thread_pool_new() -> *mut WrThreadPool {
|
||||||
let worker_config = rayon::Configuration::new()
|
let worker_config = rayon::Configuration::new()
|
||||||
.thread_name(|idx|{ format!("WebRender:Worker#{}", idx) })
|
.thread_name(|idx|{ format!("WRWorker#{}", idx) })
|
||||||
.start_handler(|idx| {
|
.start_handler(|idx| {
|
||||||
register_thread_with_profiler(format!("WebRender:Worker#{}", idx));
|
let name = format!("WRWorker#{}", idx);
|
||||||
|
register_thread_with_profiler(name.clone());
|
||||||
|
gecko_profiler_register_thread(CString::new(name).unwrap().as_ptr());
|
||||||
|
})
|
||||||
|
.exit_handler(|_idx| {
|
||||||
|
gecko_profiler_unregister_thread();
|
||||||
});
|
});
|
||||||
|
|
||||||
let workers = Arc::new(rayon::ThreadPool::new(worker_config).unwrap());
|
let workers = Arc::new(rayon::ThreadPool::new(worker_config).unwrap());
|
||||||
@ -650,6 +684,7 @@ pub extern "C" fn wr_window_new(window_id: WrWindowId,
|
|||||||
recorder: recorder,
|
recorder: recorder,
|
||||||
blob_image_renderer: Some(Box::new(Moz2dImageRenderer::new(workers.clone()))),
|
blob_image_renderer: Some(Box::new(Moz2dImageRenderer::new(workers.clone()))),
|
||||||
workers: Some(workers.clone()),
|
workers: Some(workers.clone()),
|
||||||
|
thread_listener: Some(Box::new(GeckoProfilerThreadListener::new())),
|
||||||
enable_render_on_scroll: false,
|
enable_render_on_scroll: false,
|
||||||
resource_override_path: unsafe {
|
resource_override_path: unsafe {
|
||||||
let override_charptr = gfx_wr_resource_path_override();
|
let override_charptr = gfx_wr_resource_path_override();
|
||||||
@ -662,6 +697,7 @@ pub extern "C" fn wr_window_new(window_id: WrWindowId,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
renderer_id: Some(window_id.0),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -26,6 +26,8 @@ void gfx_critical_note(const char* msg);
|
|||||||
void gfx_critical_error(const char* msg);
|
void gfx_critical_error(const char* msg);
|
||||||
void gecko_printf_stderr_output(const char* msg);
|
void gecko_printf_stderr_output(const char* msg);
|
||||||
void* get_proc_address_from_glcontext(void* glcontext_ptr, const char* procname);
|
void* get_proc_address_from_glcontext(void* glcontext_ptr, const char* procname);
|
||||||
|
void gecko_profiler_register_thread(const char* threadname);
|
||||||
|
void gecko_profiler_unregister_thread();
|
||||||
|
|
||||||
} // extern "C"
|
} // extern "C"
|
||||||
|
|
||||||
|
@ -943,6 +943,10 @@ extern void DeleteFontData(WrFontKey aKey);
|
|||||||
|
|
||||||
extern void gecko_printf_stderr_output(const char *aMsg);
|
extern void gecko_printf_stderr_output(const char *aMsg);
|
||||||
|
|
||||||
|
extern void gecko_profiler_register_thread(const char *aName);
|
||||||
|
|
||||||
|
extern void gecko_profiler_unregister_thread();
|
||||||
|
|
||||||
extern void gfx_critical_error(const char *aMsg);
|
extern void gfx_critical_error(const char *aMsg);
|
||||||
|
|
||||||
extern void gfx_critical_note(const char *aMsg);
|
extern void gfx_critical_note(const char *aMsg);
|
||||||
|
@ -3673,7 +3673,8 @@ static bool
|
|||||||
array_proto_finish(JSContext* cx, JS::HandleObject ctor, JS::HandleObject proto)
|
array_proto_finish(JSContext* cx, JS::HandleObject ctor, JS::HandleObject proto)
|
||||||
{
|
{
|
||||||
// Add Array.prototype[@@unscopables]. ECMA-262 draft (2016 Mar 19) 22.1.3.32.
|
// Add Array.prototype[@@unscopables]. ECMA-262 draft (2016 Mar 19) 22.1.3.32.
|
||||||
RootedObject unscopables(cx, NewObjectWithGivenProto<PlainObject>(cx, nullptr, TenuredObject));
|
RootedObject unscopables(cx, NewObjectWithGivenProto<PlainObject>(cx, nullptr,
|
||||||
|
SingletonObject));
|
||||||
if (!unscopables)
|
if (!unscopables)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
|
@ -181,6 +181,7 @@ void counters_reset(int) {
|
|||||||
static void
|
static void
|
||||||
InstallCoverageSignalHandlers()
|
InstallCoverageSignalHandlers()
|
||||||
{
|
{
|
||||||
|
#ifndef XP_WIN
|
||||||
fprintf(stderr, "[CodeCoverage] Setting handlers for process %d.\n", getpid());
|
fprintf(stderr, "[CodeCoverage] Setting handlers for process %d.\n", getpid());
|
||||||
|
|
||||||
struct sigaction dump_sa;
|
struct sigaction dump_sa;
|
||||||
@ -196,6 +197,7 @@ InstallCoverageSignalHandlers()
|
|||||||
sigemptyset(&reset_sa.sa_mask);
|
sigemptyset(&reset_sa.sa_mask);
|
||||||
mozilla::DebugOnly<int> r2 = sigaction(SIGUSR2, &reset_sa, nullptr);
|
mozilla::DebugOnly<int> r2 = sigaction(SIGUSR2, &reset_sa, nullptr);
|
||||||
MOZ_ASSERT(r2 == 0, "Failed to install GCOV SIGUSR2 handler");
|
MOZ_ASSERT(r2 == 0, "Failed to install GCOV SIGUSR2 handler");
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
@ -588,9 +588,9 @@ nsOpenTypeTable::MakeTextRun(DrawTarget* aDrawTarget,
|
|||||||
NSToCoordRound(aAppUnitsPerDevPixel *
|
NSToCoordRound(aAppUnitsPerDevPixel *
|
||||||
aFontGroup->GetFirstValidFont()->
|
aFontGroup->GetFirstValidFont()->
|
||||||
GetGlyphHAdvance(aDrawTarget, aGlyph.glyphID));
|
GetGlyphHAdvance(aDrawTarget, aGlyph.glyphID));
|
||||||
gfxShapedText::CompressedGlyph g;
|
textRun->SetGlyphs(0,
|
||||||
g.SetComplex(true, true, 1);
|
gfxShapedText::CompressedGlyph::MakeComplex(true, true, 1),
|
||||||
textRun->SetGlyphs(0, g, &detailedGlyph);
|
&detailedGlyph);
|
||||||
|
|
||||||
return textRun.forget();
|
return textRun.forget();
|
||||||
}
|
}
|
||||||
|
9
testing/config/tooltool-manifests/win32/ccov.manifest
Normal file
9
testing/config/tooltool-manifests/win32/ccov.manifest
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
[
|
||||||
|
{
|
||||||
|
"size": 271486,
|
||||||
|
"digest": "6956eb1cab16dff465861cd1966e3115f31b533334089553b8b94897bc138c6dec279bf7c60b34fe3fc67c8cb0d41e30f55982adaad07320090dab053e018dec",
|
||||||
|
"algorithm": "sha512",
|
||||||
|
"filename": "grcov-win-i686.tar.bz2",
|
||||||
|
"unpack": false
|
||||||
|
}
|
||||||
|
]
|
@ -5,8 +5,11 @@
|
|||||||
|
|
||||||
import os
|
import os
|
||||||
import shutil
|
import shutil
|
||||||
|
import sys
|
||||||
|
import tarfile
|
||||||
import tempfile
|
import tempfile
|
||||||
|
|
||||||
|
import mozinfo
|
||||||
from mozharness.base.script import (
|
from mozharness.base.script import (
|
||||||
PreScriptAction,
|
PreScriptAction,
|
||||||
PostScriptAction,
|
PostScriptAction,
|
||||||
@ -50,7 +53,7 @@ class CodeCoverageMixin(object):
|
|||||||
return True
|
return True
|
||||||
|
|
||||||
# XXX workaround because bug 1110465 is hard
|
# XXX workaround because bug 1110465 is hard
|
||||||
return self.buildbot_config['properties']['stage_platform'] in ('linux64-ccov',)
|
return 'ccov' in self.buildbot_config['properties']['stage_platform']
|
||||||
except (AttributeError, KeyError, TypeError):
|
except (AttributeError, KeyError, TypeError):
|
||||||
return False
|
return False
|
||||||
|
|
||||||
@ -70,7 +73,7 @@ class CodeCoverageMixin(object):
|
|||||||
return True
|
return True
|
||||||
|
|
||||||
# XXX workaround because bug 1110465 is hard
|
# XXX workaround because bug 1110465 is hard
|
||||||
return self.buildbot_config['properties']['stage_platform'] in ('linux64-jsdcov',)
|
return 'jsdcov' in self.buildbot_config['properties']['stage_platform']
|
||||||
except (AttributeError, KeyError, TypeError):
|
except (AttributeError, KeyError, TypeError):
|
||||||
return False
|
return False
|
||||||
|
|
||||||
@ -94,15 +97,24 @@ class CodeCoverageMixin(object):
|
|||||||
# Create the grcov directory, get the tooltool manifest, and finally
|
# Create the grcov directory, get the tooltool manifest, and finally
|
||||||
# download and unpack the grcov binary.
|
# download and unpack the grcov binary.
|
||||||
self.grcov_dir = tempfile.mkdtemp()
|
self.grcov_dir = tempfile.mkdtemp()
|
||||||
|
|
||||||
|
if mozinfo.os == 'linux':
|
||||||
|
platform = 'linux64'
|
||||||
|
tar_file = 'grcov-linux-standalone-x86_64.tar.bz2'
|
||||||
|
elif mozinfo.os == 'win':
|
||||||
|
platform = 'win32'
|
||||||
|
tar_file = 'grcov-win-i686.tar.bz2'
|
||||||
|
|
||||||
manifest = os.path.join(dirs.get('abs_test_install_dir', os.path.join(dirs['abs_work_dir'], 'tests')), \
|
manifest = os.path.join(dirs.get('abs_test_install_dir', os.path.join(dirs['abs_work_dir'], 'tests')), \
|
||||||
'config/tooltool-manifests/linux64/ccov.manifest')
|
'config/tooltool-manifests/%s/ccov.manifest' % platform)
|
||||||
|
|
||||||
tooltool_path = self._fetch_tooltool_py()
|
tooltool_path = self._fetch_tooltool_py()
|
||||||
cmd = [tooltool_path, '--url', 'https://tooltool.mozilla-releng.net/', 'fetch', \
|
cmd = [sys.executable, tooltool_path, '--url', 'https://tooltool.mozilla-releng.net/', 'fetch', \
|
||||||
'-m', manifest, '-o', '-c', '/builds/worker/tooltool-cache']
|
'-m', manifest, '-o', '-c', '/builds/worker/tooltool-cache']
|
||||||
self.run_command(cmd, cwd=self.grcov_dir)
|
self.run_command(cmd, cwd=self.grcov_dir)
|
||||||
self.run_command(['tar', '-jxvf', os.path.join(self.grcov_dir, 'grcov-linux-standalone-x86_64.tar.bz2'), \
|
|
||||||
'-C', self.grcov_dir], cwd=self.grcov_dir)
|
with tarfile.open(os.path.join(self.grcov_dir, tar_file)) as tar:
|
||||||
|
tar.extractall(self.grcov_dir)
|
||||||
|
|
||||||
@PostScriptAction('run-tests')
|
@PostScriptAction('run-tests')
|
||||||
def _package_coverage_data(self, action, success=None):
|
def _package_coverage_data(self, action, success=None):
|
||||||
@ -126,6 +138,7 @@ class CodeCoverageMixin(object):
|
|||||||
|
|
||||||
if not self.code_coverage_enabled:
|
if not self.code_coverage_enabled:
|
||||||
return
|
return
|
||||||
|
|
||||||
del os.environ['GCOV_PREFIX']
|
del os.environ['GCOV_PREFIX']
|
||||||
del os.environ['JS_CODE_COVERAGE_OUTPUT_DIR']
|
del os.environ['JS_CODE_COVERAGE_OUTPUT_DIR']
|
||||||
|
|
||||||
@ -140,50 +153,59 @@ class CodeCoverageMixin(object):
|
|||||||
if any(d in dirs for d in canary_dirs):
|
if any(d in dirs for d in canary_dirs):
|
||||||
rel_topsrcdir = root
|
rel_topsrcdir = root
|
||||||
break
|
break
|
||||||
else:
|
|
||||||
|
if rel_topsrcdir is None:
|
||||||
# Unable to upload code coverage files. Since this is the whole
|
# Unable to upload code coverage files. Since this is the whole
|
||||||
# point of code coverage, making this fatal.
|
# point of code coverage, making this fatal.
|
||||||
self.fatal("Could not find relative topsrcdir in code coverage "
|
self.fatal("Could not find relative topsrcdir in code coverage data!")
|
||||||
"data!")
|
|
||||||
|
dirs = self.query_abs_dirs()
|
||||||
|
|
||||||
# Package GCOV coverage data.
|
# Package GCOV coverage data.
|
||||||
dirs = self.query_abs_dirs()
|
file_path_gcda = os.path.join(dirs['abs_blob_upload_dir'], 'code-coverage-gcda.zip')
|
||||||
file_path_gcda = os.path.join(
|
self.run_command(['zip', '-r', file_path_gcda, '.'], cwd=rel_topsrcdir)
|
||||||
dirs['abs_blob_upload_dir'], 'code-coverage-gcda.zip')
|
|
||||||
command = ['zip', '-r', file_path_gcda, '.']
|
|
||||||
self.run_command(command, cwd=rel_topsrcdir)
|
|
||||||
|
|
||||||
# Package JSVM coverage data.
|
# Package JSVM coverage data.
|
||||||
dirs = self.query_abs_dirs()
|
file_path_jsvm = os.path.join(dirs['abs_blob_upload_dir'], 'code-coverage-jsvm.zip')
|
||||||
file_path_jsvm = os.path.join(
|
self.run_command(['zip', '-r', file_path_jsvm, '.'], cwd=self.jsvm_dir)
|
||||||
dirs['abs_blob_upload_dir'], 'code-coverage-jsvm.zip')
|
|
||||||
command = ['zip', '-r', file_path_jsvm, '.']
|
|
||||||
self.run_command(command, cwd=self.jsvm_dir)
|
|
||||||
|
|
||||||
# GRCOV post-processing
|
# GRCOV post-processing
|
||||||
# Download the gcno fom the build machine.
|
# Download the gcno fom the build machine.
|
||||||
self.download_file(self.url_to_gcno, file_name=None, parent_dir=self.grcov_dir)
|
self.download_file(self.url_to_gcno, file_name=None, parent_dir=self.grcov_dir)
|
||||||
|
|
||||||
|
if mozinfo.os == 'linux':
|
||||||
|
prefix = '/builds/worker/workspace/build/src/'
|
||||||
|
elif mozinfo.os == 'win':
|
||||||
|
prefix = 'z:/build/build/src/'
|
||||||
|
|
||||||
# Run grcov on the zipped .gcno and .gcda files.
|
# Run grcov on the zipped .gcno and .gcda files.
|
||||||
grcov_command = [
|
grcov_command = [
|
||||||
os.path.join(self.grcov_dir, 'grcov'),
|
os.path.join(self.grcov_dir, 'grcov'),
|
||||||
'-t', 'lcov',
|
'-t', 'lcov',
|
||||||
'-p', '/builds/worker/workspace/build/src/',
|
'-p', prefix,
|
||||||
'--ignore-dir', 'gcc',
|
'--ignore-dir', 'gcc',
|
||||||
os.path.join(self.grcov_dir, 'target.code-coverage-gcno.zip'), file_path_gcda
|
os.path.join(self.grcov_dir, 'target.code-coverage-gcno.zip'), file_path_gcda
|
||||||
]
|
]
|
||||||
|
|
||||||
# 'grcov_output' will be a tuple, the first variable is the path to the lcov output,
|
# 'grcov_output' will be a tuple, the first variable is the path to the lcov output,
|
||||||
# the other is the path to the standard error output.
|
# the other is the path to the standard error output.
|
||||||
grcov_output = self.get_output_from_command(grcov_command, cwd=self.grcov_dir, \
|
grcov_output = self.get_output_from_command(
|
||||||
silent=True, tmpfile_base_path=os.path.join(self.grcov_dir, 'grcov_lcov_output'), \
|
grcov_command,
|
||||||
save_tmpfiles=True, return_type='files')
|
cwd=self.grcov_dir,
|
||||||
|
silent=True,
|
||||||
|
tmpfile_base_path=os.path.join(self.grcov_dir, 'grcov_lcov_output'),
|
||||||
|
save_tmpfiles=True,
|
||||||
|
return_type='files'
|
||||||
|
)
|
||||||
new_output_name = grcov_output[0] + '.info'
|
new_output_name = grcov_output[0] + '.info'
|
||||||
os.rename(grcov_output[0], new_output_name)
|
os.rename(grcov_output[0], new_output_name)
|
||||||
|
|
||||||
# Zip the grcov output and upload it.
|
# Zip the grcov output and upload it.
|
||||||
command = ['zip', os.path.join(dirs['abs_blob_upload_dir'], 'code-coverage-grcov.zip'), new_output_name]
|
self.run_command(
|
||||||
self.run_command(command, cwd=self.grcov_dir)
|
['zip', os.path.join(dirs['abs_blob_upload_dir'], 'code-coverage-grcov.zip'), new_output_name],
|
||||||
|
cwd=self.grcov_dir
|
||||||
|
)
|
||||||
|
|
||||||
shutil.rmtree(self.gcov_dir)
|
shutil.rmtree(self.gcov_dir)
|
||||||
shutil.rmtree(self.jsvm_dir)
|
shutil.rmtree(self.jsvm_dir)
|
||||||
shutil.rmtree(self.grcov_dir)
|
shutil.rmtree(self.grcov_dir)
|
||||||
|
@ -3,9 +3,11 @@
|
|||||||
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
* 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/. */
|
* You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
#include <signal.h>
|
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
|
#ifndef XP_WIN
|
||||||
|
#include <signal.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
|
#endif
|
||||||
#include "mozilla/CodeCoverageHandler.h"
|
#include "mozilla/CodeCoverageHandler.h"
|
||||||
#include "mozilla/ClearOnShutdown.h"
|
#include "mozilla/ClearOnShutdown.h"
|
||||||
#include "mozilla/DebugOnly.h"
|
#include "mozilla/DebugOnly.h"
|
||||||
@ -62,6 +64,7 @@ void CodeCoverageHandler::ResetCounters(int)
|
|||||||
|
|
||||||
void CodeCoverageHandler::SetSignalHandlers()
|
void CodeCoverageHandler::SetSignalHandlers()
|
||||||
{
|
{
|
||||||
|
#ifndef XP_WIN
|
||||||
printf_stderr("[CodeCoverage] Setting handlers for process %d.\n", getpid());
|
printf_stderr("[CodeCoverage] Setting handlers for process %d.\n", getpid());
|
||||||
|
|
||||||
struct sigaction dump_sa;
|
struct sigaction dump_sa;
|
||||||
@ -77,6 +80,7 @@ void CodeCoverageHandler::SetSignalHandlers()
|
|||||||
sigemptyset(&reset_sa.sa_mask);
|
sigemptyset(&reset_sa.sa_mask);
|
||||||
DebugOnly<int> r2 = sigaction(SIGUSR2, &reset_sa, nullptr);
|
DebugOnly<int> r2 = sigaction(SIGUSR2, &reset_sa, nullptr);
|
||||||
MOZ_ASSERT(r2 == 0, "Failed to install GCOV SIGUSR2 handler");
|
MOZ_ASSERT(r2 == 0, "Failed to install GCOV SIGUSR2 handler");
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
CodeCoverageHandler::CodeCoverageHandler()
|
CodeCoverageHandler::CodeCoverageHandler()
|
||||||
|
Loading…
Reference in New Issue
Block a user