Merge mozilla-central to mozilla-inbound

This commit is contained in:
Carsten "Tomcat" Book 2015-02-11 15:00:07 +01:00
Родитель 0f22d4de42 1da4eb2ea5
Коммит 1ebb197830
66 изменённых файлов: 1004 добавлений и 449 удалений

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

@ -15,7 +15,7 @@
<project name="platform_build" path="build" remote="b2g" revision="f47964c89a9c34d9a2e77afa3608d9ec604c3d20">
<copyfile dest="Makefile" src="core/root.mk"/>
</project>
<project name="gaia" path="gaia" remote="mozillaorg" revision="8c7865486a1b11076b849bbf8f7fccbaffbfafe7"/>
<project name="gaia" path="gaia" remote="mozillaorg" revision="462a2ef9e98134255c144e373c7392440e3ee03b"/>
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="2262d4a77d4f46ab230fd747bb91e9b77bad36cb"/>
<project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/>
@ -134,9 +134,9 @@
<project name="platform/hardware/akm" path="hardware/akm" revision="6d3be412647b0eab0adff8a2768736cf4eb68039"/>
<project groups="invensense" name="platform/hardware/invensense" path="hardware/invensense" revision="e6d9ab28b4f4e7684f6c07874ee819c9ea0002a2"/>
<project name="platform/hardware/ril" path="hardware/ril" revision="865ce3b4a2ba0b3a31421ca671f4d6c5595f8690"/>
<project name="kernel/common" path="kernel" revision="b1b623fcc5c8a22f4e9cb30470378496e03c9404"/>
<project name="kernel/common" path="kernel" revision="0b4249f88cb8faadfbf80be207af14198dec08e3"/>
<project name="platform/system/core" path="system/core" revision="53d584d4a4b4316e4de9ee5f210d662f89b44e7e"/>
<project name="u-boot" path="u-boot" revision="64b93dbec64421a05bc7ffdbc03917700d171570"/>
<project name="u-boot" path="u-boot" revision="6980cf8f8cf9c1d43ff92b7af13425a5ed531d12"/>
<project name="vendor/sprd/gps" path="vendor/sprd/gps" revision="6974f8e771d4d8e910357a6739ab124768891e8f"/>
<project name="vendor/sprd/open-source" path="vendor/sprd/open-source" revision="f56ab768cb9f1ad42fb0809ffec1424b1e693369"/>
<project name="vendor/sprd/partner" path="vendor/sprd/partner" revision="8649c7145972251af11b0639997edfecabfc7c2e"/>

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

@ -19,7 +19,7 @@
<copyfile dest="Makefile" src="core/root.mk"/>
</project>
<project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="8c7865486a1b11076b849bbf8f7fccbaffbfafe7"/>
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="462a2ef9e98134255c144e373c7392440e3ee03b"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="2262d4a77d4f46ab230fd747bb91e9b77bad36cb"/>
<project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
<project name="platform_hardware_ril" path="hardware/ril" remote="b2g" revision="eb1795a9002eb142ac58c8d68f8f4ba094af07ca"/>

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

@ -17,7 +17,7 @@
</project>
<project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
<project name="gaia" path="gaia" remote="mozillaorg" revision="8c7865486a1b11076b849bbf8f7fccbaffbfafe7"/>
<project name="gaia" path="gaia" remote="mozillaorg" revision="462a2ef9e98134255c144e373c7392440e3ee03b"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="2262d4a77d4f46ab230fd747bb91e9b77bad36cb"/>
<project name="moztt" path="external/moztt" remote="b2g" revision="fe893bb760a3bb64375f62fdf4762a58c59df9ef"/>
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="da777082e02eec11c4b7e27679bdb15f47a44f66"/>

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

@ -15,7 +15,7 @@
<project name="platform_build" path="build" remote="b2g" revision="f47964c89a9c34d9a2e77afa3608d9ec604c3d20">
<copyfile dest="Makefile" src="core/root.mk"/>
</project>
<project name="gaia" path="gaia" remote="mozillaorg" revision="8c7865486a1b11076b849bbf8f7fccbaffbfafe7"/>
<project name="gaia" path="gaia" remote="mozillaorg" revision="462a2ef9e98134255c144e373c7392440e3ee03b"/>
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="2262d4a77d4f46ab230fd747bb91e9b77bad36cb"/>
<project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/>

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

@ -19,7 +19,7 @@
<copyfile dest="Makefile" src="core/root.mk"/>
</project>
<project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="8c7865486a1b11076b849bbf8f7fccbaffbfafe7"/>
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="462a2ef9e98134255c144e373c7392440e3ee03b"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="2262d4a77d4f46ab230fd747bb91e9b77bad36cb"/>
<project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
<project name="platform_hardware_ril" path="hardware/ril" remote="b2g" revision="eb1795a9002eb142ac58c8d68f8f4ba094af07ca"/>

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

@ -15,7 +15,7 @@
<project name="platform_build" path="build" remote="b2g" revision="f47964c89a9c34d9a2e77afa3608d9ec604c3d20">
<copyfile dest="Makefile" src="core/root.mk"/>
</project>
<project name="gaia" path="gaia" remote="mozillaorg" revision="8c7865486a1b11076b849bbf8f7fccbaffbfafe7"/>
<project name="gaia" path="gaia" remote="mozillaorg" revision="462a2ef9e98134255c144e373c7392440e3ee03b"/>
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="2262d4a77d4f46ab230fd747bb91e9b77bad36cb"/>
<project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/>

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

@ -17,7 +17,7 @@
</project>
<project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/>
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
<project name="gaia" path="gaia" remote="mozillaorg" revision="8c7865486a1b11076b849bbf8f7fccbaffbfafe7"/>
<project name="gaia" path="gaia" remote="mozillaorg" revision="462a2ef9e98134255c144e373c7392440e3ee03b"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="2262d4a77d4f46ab230fd747bb91e9b77bad36cb"/>
<project name="moztt" path="external/moztt" remote="b2g" revision="fe893bb760a3bb64375f62fdf4762a58c59df9ef"/>
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="da777082e02eec11c4b7e27679bdb15f47a44f66"/>

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

@ -1,9 +1,9 @@
{
"git": {
"git_revision": "8c7865486a1b11076b849bbf8f7fccbaffbfafe7",
"git_revision": "462a2ef9e98134255c144e373c7392440e3ee03b",
"remote": "https://git.mozilla.org/releases/gaia.git",
"branch": ""
},
"revision": "c97ae9ec825f591d669e8b7128b81493b88dcf3f",
"revision": "e6a1cfc70305b3695b694d7e66b03c51f2a45b1f",
"repo_path": "integration/gaia-central"
}

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

@ -17,7 +17,7 @@
</project>
<project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
<project name="gaia" path="gaia" remote="mozillaorg" revision="8c7865486a1b11076b849bbf8f7fccbaffbfafe7"/>
<project name="gaia" path="gaia" remote="mozillaorg" revision="462a2ef9e98134255c144e373c7392440e3ee03b"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="2262d4a77d4f46ab230fd747bb91e9b77bad36cb"/>
<project name="moztt" path="external/moztt" remote="b2g" revision="fe893bb760a3bb64375f62fdf4762a58c59df9ef"/>
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="da777082e02eec11c4b7e27679bdb15f47a44f66"/>

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

@ -15,7 +15,7 @@
<project name="platform_build" path="build" remote="b2g" revision="7f2ee9f4cb926684883fc2a2e407045fd9db2199">
<copyfile dest="Makefile" src="core/root.mk"/>
</project>
<project name="gaia" path="gaia" remote="mozillaorg" revision="8c7865486a1b11076b849bbf8f7fccbaffbfafe7"/>
<project name="gaia" path="gaia" remote="mozillaorg" revision="462a2ef9e98134255c144e373c7392440e3ee03b"/>
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="2262d4a77d4f46ab230fd747bb91e9b77bad36cb"/>
<project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/>

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

@ -24,6 +24,7 @@ support-files =
[browser_perf-details-01.js]
[browser_perf-details-02.js]
[browser_perf-details-03.js]
[browser_perf-details-04.js]
[browser_perf-events-calltree.js]
[browser_perf-front-basic-profiler-01.js]
[browser_perf-front-basic-timeline-01.js]

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

@ -1,30 +1,31 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
let MEMORY_PREF = "devtools.performance.ui.enable-memory";
/**
* Tests that the details view hides the memory buttons when `enable-memory` is toggled,
* and that it switches to default panel if toggling while a memory panel is selected.
*/
function spawnTest () {
let { panel } = yield initPerformance(SIMPLE_URL);
let { EVENTS, DetailsView } = panel.panelWin;
let { EVENTS, PerformanceController, OverviewView, DetailsView } = panel.panelWin;
let { $, WaterfallView, MemoryCallTreeView, MemoryFlameGraphView } = panel.panelWin;
Services.prefs.setBoolPref(MEMORY_PREF, false);
ok(DetailsView.isViewSelected(WaterfallView),
"The waterfall view is selected by default in the details view.");
// The toolbar buttons will always be hidden when a recording isn't available,
// so make sure we have one that's finished.
yield startRecording(panel);
yield stopRecording(panel);
let flameBtn = $("toolbarbutton[data-view='memory-flamegraph']");
let callBtn = $("toolbarbutton[data-view='memory-calltree']");
Services.prefs.setBoolPref(MEMORY_PREF, false);
is(flameBtn.hidden, true, "memory-flamegraph button hidden when enable-memory=false");
is(callBtn.hidden, true, "memory-calltree button hidden when enable-memory=false");
Services.prefs.setBoolPref(MEMORY_PREF, true);
is(flameBtn.hidden, false, "memory-flamegraph button shown when enable-memory=true");
is(callBtn.hidden, false, "memory-calltree button shown when enable-memory=true");
@ -33,6 +34,17 @@ function spawnTest () {
DetailsView.selectView("memory-calltree");
yield Promise.all([selected, notified]);
ok(DetailsView.isViewSelected(MemoryCallTreeView),
"The memory call tree view can now be selected.");
selected = DetailsView.whenViewSelected(MemoryFlameGraphView);
notified = DetailsView.once(EVENTS.DETAILS_VIEW_SELECTED);
DetailsView.selectView("memory-flamegraph");
yield Promise.all([selected, notified]);
ok(DetailsView.isViewSelected(MemoryFlameGraphView),
"The memory flamegraph view can now be selected.");
selected = DetailsView.whenViewSelected(WaterfallView);
notified = DetailsView.once(EVENTS.DETAILS_VIEW_SELECTED);
Services.prefs.setBoolPref(MEMORY_PREF, false);
@ -43,16 +55,30 @@ function spawnTest () {
Services.prefs.setBoolPref(MEMORY_PREF, true);
selected = DetailsView.whenViewSelected(MemoryCallTreeView);
notified = DetailsView.once(EVENTS.DETAILS_VIEW_SELECTED);
DetailsView.selectView("memory-calltree");
yield Promise.all([selected, notified]);
ok(DetailsView.isViewSelected(MemoryCallTreeView),
"The memory call tree view can be selected again after re-enabling memory.");
selected = DetailsView.whenViewSelected(MemoryFlameGraphView);
notified = DetailsView.once(EVENTS.DETAILS_VIEW_SELECTED);
DetailsView.selectView("memory-flamegraph");
yield Promise.all([selected, notified]);
ok(DetailsView.isViewSelected(MemoryFlameGraphView),
"The memory flamegraph view can be selected again after re-enabling memory.");
selected = DetailsView.whenViewSelected(WaterfallView);
notified = DetailsView.once(EVENTS.DETAILS_VIEW_SELECTED);
Services.prefs.setBoolPref(MEMORY_PREF, false);
yield Promise.all([selected, notified]);
ok(DetailsView.isViewSelected(WaterfallView),
"The waterfall view is now selected when toggling off enable-memory when a memory panel is selected.");
yield teardown(panel);
finish();
}

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

@ -0,0 +1,87 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
/**
* Tests that the details view hides the toolbar buttons when a recording
* doesn't exist or is in progress.
*/
function spawnTest () {
let { panel } = yield initPerformance(SIMPLE_URL);
let { EVENTS, $, $$, PerformanceController, RecordingsView, DetailsView } = panel.panelWin;
let waterfallBtn = $("toolbarbutton[data-view='waterfall']");
let jsFlameBtn = $("toolbarbutton[data-view='js-flamegraph']");
let jsCallBtn = $("toolbarbutton[data-view='js-calltree']");
let memFlameBtn = $("toolbarbutton[data-view='memory-flamegraph']");
let memCallBtn = $("toolbarbutton[data-view='memory-calltree']");
is(waterfallBtn.hidden, true, "waterfall button hidden when tool starts.");
is(jsFlameBtn.hidden, true, "js-flamegraph button hidden when tool starts.");
is(jsCallBtn.hidden, true, "js-calltree button hidden when tool starts.");
is(memFlameBtn.hidden, true, "memory-flamegraph button hidden when tool starts.");
is(memCallBtn.hidden, true, "memory-calltree button hidden when tool starts.");
yield startRecording(panel);
is(waterfallBtn.hidden, true, "waterfall button hidden when recording starts.");
is(jsFlameBtn.hidden, true, "js-flamegraph button hidden when recording starts.");
is(jsCallBtn.hidden, true, "js-calltree button hidden when recording starts.");
is(memFlameBtn.hidden, true, "memory-flamegraph button hidden when recording starts.");
is(memCallBtn.hidden, true, "memory-calltree button hidden when recording starts.");
yield stopRecording(panel);
is(waterfallBtn.hidden, false, "waterfall button visible when recording ends.");
is(jsFlameBtn.hidden, false, "js-flamegraph button visible when recording ends.");
is(jsCallBtn.hidden, false, "js-calltree button visible when recording ends.");
is(memFlameBtn.hidden, true, "memory-flamegraph button hidden when recording ends.");
is(memCallBtn.hidden, true, "memory-calltree button hidden when recording ends.");
yield startRecording(panel);
is(waterfallBtn.hidden, true, "waterfall button hidden when another recording starts.");
is(jsFlameBtn.hidden, true, "js-flamegraph button hidden when another recording starts.");
is(jsCallBtn.hidden, true, "js-calltree button hidden when another recording starts.");
is(memFlameBtn.hidden, true, "memory-flamegraph button hidden when another recording starts.");
is(memCallBtn.hidden, true, "memory-calltree button hidden when another recording starts.");
let select = once(PerformanceController, EVENTS.RECORDING_SELECTED);
mousedown(panel.panelWin, $$(".recording-item")[0]);
yield select;
is(RecordingsView.selectedIndex, 0,
"The first recording was selected again.");
is(waterfallBtn.hidden, false, "waterfall button visible when first recording selected.");
is(jsFlameBtn.hidden, false, "js-flamegraph button visible when first recording selected.");
is(jsCallBtn.hidden, false, "js-calltree button visible when first recording selected.");
is(memFlameBtn.hidden, true, "memory-flamegraph button hidden when first recording selected.");
is(memCallBtn.hidden, true, "memory-calltree button hidden when first recording selected.");
select = once(PerformanceController, EVENTS.RECORDING_SELECTED);
mousedown(panel.panelWin, $$(".recording-item")[1]);
yield select;
is(RecordingsView.selectedIndex, 1,
"The second recording was selected again.");
is(waterfallBtn.hidden, true, "waterfall button still hidden when second recording selected.");
is(jsFlameBtn.hidden, true, "js-flamegraph button still hidden when second recording selected.");
is(jsCallBtn.hidden, true, "js-calltree button still hidden when second recording selected.");
is(memFlameBtn.hidden, true, "memory-flamegraph button still hidden when second recording selected.");
is(memCallBtn.hidden, true, "memory-calltree button still hidden when second recording selected.");
yield stopRecording(panel);
is(RecordingsView.selectedIndex, 1,
"The second recording is still selected.");
is(waterfallBtn.hidden, false, "waterfall button visible when second recording finished.");
is(jsFlameBtn.hidden, false, "js-flamegraph button visible when second recording finished.");
is(jsCallBtn.hidden, false, "js-calltree button visible when second recording finished.");
is(memFlameBtn.hidden, true, "memory-flamegraph button hidden when second recording finished.");
is(memCallBtn.hidden, true, "memory-calltree button hidden when second recording finished.");
yield teardown(panel);
finish();
}

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

@ -8,6 +8,9 @@ function spawnTest () {
let { panel } = yield initPerformance(SIMPLE_URL);
let { EVENTS, DetailsView, MemoryCallTreeView } = panel.panelWin;
// Enable memory to test.
Services.prefs.setBoolPref(MEMORY_PREF, true);
yield DetailsView.selectView("memory-calltree");
ok(DetailsView.isViewSelected(MemoryCallTreeView), "The call tree is now selected.");

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

@ -8,6 +8,9 @@ function spawnTest () {
let { panel } = yield initPerformance(SIMPLE_URL);
let { EVENTS, DetailsView, MemoryFlameGraphView } = panel.panelWin;
// Enable memory to test.
Services.prefs.setBoolPref(MEMORY_PREF, true);
yield DetailsView.selectView("memory-flamegraph");
ok(DetailsView.isViewSelected(MemoryFlameGraphView), "The flamegraph is now selected.");

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

@ -262,6 +262,10 @@ function click (win, button) {
EventUtils.sendMouseEvent({ type: "click" }, button, win);
}
function mousedown (win, button) {
EventUtils.sendMouseEvent({ type: "mousedown" }, button, win);
}
function* startRecording(panel) {
let win = panel.panelWin;
let clicked = panel.panelWin.PerformanceView.once(win.EVENTS.UI_START_RECORDING);

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

@ -29,6 +29,7 @@ let DetailsView = {
this.toolbar = $("#performance-toolbar-controls-detail-views");
this._onViewToggle = this._onViewToggle.bind(this);
this._onRecordingStoppedOrSelected = this._onRecordingStoppedOrSelected.bind(this);
this.setAvailableViews = this.setAvailableViews.bind(this);
for (let button of $$("toolbarbutton[data-view]", this.toolbar)) {
@ -36,8 +37,10 @@ let DetailsView = {
}
yield this.selectView(DEFAULT_DETAILS_SUBVIEW);
this.setAvailableViews();
yield this.setAvailableViews();
PerformanceController.on(EVENTS.RECORDING_STOPPED, this._onRecordingStoppedOrSelected);
PerformanceController.on(EVENTS.RECORDING_SELECTED, this._onRecordingStoppedOrSelected);
PerformanceController.on(EVENTS.PREF_CHANGED, this.setAvailableViews);
}),
@ -53,6 +56,8 @@ let DetailsView = {
component.initialized && (yield component.view.destroy());
}
PerformanceController.off(EVENTS.RECORDING_STOPPED, this._onRecordingStoppedOrSelected);
PerformanceController.off(EVENTS.RECORDING_SELECTED, this._onRecordingStoppedOrSelected);
PerformanceController.off(EVENTS.PREF_CHANGED, this.setAvailableViews);
}),
@ -61,21 +66,21 @@ let DetailsView = {
* buttons that select them and going to default view if currently selected.
* Called when a preference changes in `devtools.performance.ui.`.
*/
setAvailableViews: function () {
setAvailableViews: Task.async(function* () {
for (let [name, { view, pref }] of Iterator(this.components)) {
if (!pref) {
continue;
}
let value = PerformanceController.getPref(pref);
$(`toolbarbutton[data-view=${name}]`).hidden = !value;
let recording = PerformanceController.getCurrentRecording();
let isRecorded = recording && !recording.isRecording();
let isEnabled = !pref || PerformanceController.getPref(pref);
$(`toolbarbutton[data-view=${name}]`).hidden = !isRecorded || !isEnabled;
// If the view is currently selected and not enabled, go back to the
// default view.
if (!value && this.isViewSelected(view)) {
this.selectView(DEFAULT_DETAILS_SUBVIEW);
if (!isEnabled && this.isViewSelected(view)) {
yield this.selectView(DEFAULT_DETAILS_SUBVIEW);
}
}
},
}),
/**
* Select one of the DetailView's subviews to be rendered,
@ -159,6 +164,13 @@ let DetailsView = {
}
}),
/**
* Called when recording stops or is selected.
*/
_onRecordingStoppedOrSelected: function(_, recording) {
this.setAvailableViews();
},
/**
* Called when a view button is clicked.
*/

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

@ -397,7 +397,7 @@ EventDispatcher::Dispatch(nsISupports* aTarget,
nsIDOMEvent* aDOMEvent,
nsEventStatus* aEventStatus,
EventDispatchingCallback* aCallback,
nsCOMArray<EventTarget>* aTargets)
nsTArray<EventTarget*>* aTargets)
{
PROFILER_LABEL("EventDispatcher", "Dispatch",
js::ProfileEntry::Category::EVENTS);
@ -476,7 +476,7 @@ EventDispatcher::Dispatch(nsISupports* aTarget,
}
#ifdef DEBUG
if (!nsContentUtils::IsSafeToRunScript()) {
if (aEvent->message != NS_EVENT_NULL && !nsContentUtils::IsSafeToRunScript()) {
nsresult rv = NS_ERROR_FAILURE;
if (target->GetContextForEventHandlers(&rv) ||
NS_FAILED(rv)) {
@ -625,7 +625,7 @@ EventDispatcher::Dispatch(nsISupports* aTarget,
aTargets->Clear();
aTargets->SetCapacity(chain.Length());
for (uint32_t i = 0; i < chain.Length(); ++i) {
aTargets->AppendObject(chain[i].CurrentTarget()->GetTargetForDOMEvent());
aTargets->AppendElement(chain[i].CurrentTarget()->GetTargetForDOMEvent());
}
} else {
// Event target chain is created. Handle the chain.

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

@ -255,7 +255,7 @@ public:
nsIDOMEvent* aDOMEvent = nullptr,
nsEventStatus* aEventStatus = nullptr,
EventDispatchingCallback* aCallback = nullptr,
nsCOMArray<dom::EventTarget>* aTargets = nullptr);
nsTArray<dom::EventTarget*>* aTargets = nullptr);
/**
* Dispatches an event.

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

@ -171,11 +171,11 @@ EventListenerService::GetEventTargetChainFor(nsIDOMEventTarget* aEventTarget,
*aOutArray = nullptr;
NS_ENSURE_ARG(aEventTarget);
WidgetEvent event(true, NS_EVENT_NULL);
nsCOMArray<EventTarget> targets;
nsTArray<EventTarget*> targets;
nsresult rv = EventDispatcher::Dispatch(aEventTarget, nullptr, &event,
nullptr, nullptr, nullptr, &targets);
NS_ENSURE_SUCCESS(rv, rv);
int32_t count = targets.Count();
int32_t count = targets.Length();
if (count == 0) {
return NS_OK;
}

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

@ -134,12 +134,51 @@ function defineLazyRegExp(obj, name, pattern) {
});
}
function NetworkInterfaceLinks()
{
this.resetLinks();
}
NetworkInterfaceLinks.prototype = {
linkRoutes: null,
gateways: null,
interfaceName: null,
extraRoutes: null,
setLinks: function(linkRoutes, gateways, interfaceName) {
this.linkRoutes = linkRoutes;
this.gateways = gateways;
this.interfaceName = interfaceName;
},
resetLinks: function() {
this.linkRoutes = [];
this.gateways = [];
this.interfaceName = "";
this.extraRoutes = [];
},
compareGateways: function(gateways) {
if (this.gateways.length != gateways.length) {
return false;
}
for (let i = 0; i < this.gateways.length; i++) {
if (this.gateways[i] != gateways[i]) {
return false;
}
}
return true;
}
};
/**
* This component watches for network interfaces changing state and then
* adjusts routes etc. accordingly.
*/
function NetworkManager() {
this.networkInterfaces = {};
this.networkInterfaceLinks = {};
Services.obs.addObserver(this, TOPIC_XPCOM_SHUTDOWN, false);
Services.obs.addObserver(this, TOPIC_MOZSETTINGS_CHANGED, false);
@ -303,6 +342,7 @@ NetworkManager.prototype = {
Cr.NS_ERROR_INVALID_ARG);
}
this.networkInterfaces[networkId] = network;
this.networkInterfaceLinks[networkId] = new NetworkInterfaceLinks();
if (network.type == Ci.nsINetworkInterface.NETWORK_TYPE_MOBILE_DUN) {
debug("Force setting " + SETTINGS_DUN_REQUIRED + " to true.");
@ -320,7 +360,10 @@ NetworkManager.prototype = {
for (let i = 0; i < length; i++) {
debug('Adding subnet routes: ' + ips.value[i] + '/' + prefixLengths.value[i]);
gNetworkService.modifyRoute(Ci.nsINetworkService.MODIFY_ROUTE_ADD,
network.name, ips.value[i], prefixLengths.value[i]);
network.name, ips.value[i], prefixLengths.value[i])
.catch((aError) => {
debug("_addSubnetRoutes error: " + aError);
});
}
},
@ -346,8 +389,16 @@ NetworkManager.prototype = {
gNetworkService.createNetwork(network.name, () => {
// Add host route for data calls
if (this.isNetworkTypeMobile(network.type)) {
gNetworkService.removeHostRoutes(network.name);
this.setHostRoutes(network);
let currentInterfaceLinks = this.networkInterfaceLinks[networkId];
let newLinkRoutes = network.getDnses().concat(network.httpProxyHost);
// If gateways have changed, remove all old routes first.
this._handleGateways(networkId, network.getGateways())
.then(() => this._updateRoutes(currentInterfaceLinks.linkRoutes,
newLinkRoutes,
network.getGateways(), network.name))
.then(() => currentInterfaceLinks.setLinks(newLinkRoutes,
network.getGateways(),
network.name));
}
// Remove pre-created default route and let setAndConfigureActive()
@ -386,7 +437,7 @@ NetworkManager.prototype = {
case Ci.nsINetworkInterface.NETWORK_STATE_DISCONNECTED:
// Remove host route for data calls
if (this.isNetworkTypeMobile(network.type)) {
this.removeHostRoutes(network);
this._cleanupAllHostRoutes(networkId);
}
// Remove secondary default route for dun.
if (network.type == Ci.nsINetworkInterface.NETWORK_TYPE_MOBILE_DUN) {
@ -435,6 +486,13 @@ NetworkManager.prototype = {
throw Components.Exception("No network with that type registered.",
Cr.NS_ERROR_INVALID_ARG);
}
// This is for in case a network gets unregistered without being
// DISCONNECTED.
if (this.isNetworkTypeMobile(network.type)) {
this._cleanupAllHostRoutes(networkId);
}
delete this.networkInterfaces[networkId];
if (network.type == Ci.nsINetworkInterface.NETWORK_TYPE_MOBILE_DUN) {
@ -450,6 +508,8 @@ NetworkManager.prototype = {
networkInterfaces: null,
networkInterfaceLinks: null,
_dataDefaultServiceId: null,
_preferredNetworkType: DEFAULT_PREFERRED_NETWORK_TYPE,
@ -477,7 +537,24 @@ NetworkManager.prototype = {
this.setAndConfigureActive();
},
_updateRoutes: function(doAdd, ipAddresses, networkName, gateways) {
_updateRoutes: function(oldLinks, newLinks, gateways, interfaceName) {
// Returns items that are in base but not in target.
function getDifference(base, target) {
return base.filter(function(i) { return target.indexOf(i) < 0; });
}
let addedLinks = getDifference(newLinks, oldLinks);
let removedLinks = getDifference(oldLinks, newLinks);
if (addedLinks.length === 0 && removedLinks.length === 0) {
return Promise.resolve();
}
return this._setHostRoutes(false, removedLinks, interfaceName, gateways)
.then(this._setHostRoutes(true, addedLinks, interfaceName, gateways));
},
_setHostRoutes: function(doAdd, ipAddresses, networkName, gateways) {
let getMaxPrefixLength = (aIp) => {
return aIp.match(this.REGEXP_IPV4) ? IPV4_MAX_PREFIX_LENGTH : IPV6_MAX_PREFIX_LENGTH;
}
@ -517,10 +594,20 @@ NetworkManager.prototype = {
}
return this.resolveHostname(network, host)
.then((ipAddresses) => this._updateRoutes(true,
ipAddresses,
network.name,
network.getGateways()));
.then((ipAddresses) => {
let promises = [];
let networkId = this.getNetworkId(network);
ipAddresses.forEach((aIpAddress) => {
let promise =
this._setHostRoutes(true, [aIpAddress], network.name, network.getGateways())
.then(() => this.networkInterfaceLinks[networkId].extraRoutes.push(aIpAddress));
promises.push(promise);
});
return Promise.all(promises);
});
},
removeHostRoute: function(network, host) {
@ -529,10 +616,29 @@ NetworkManager.prototype = {
}
return this.resolveHostname(network, host)
.then((ipAddresses) => this._updateRoutes(false,
ipAddresses,
network.name,
network.getGateways()));
.then((ipAddresses) => {
let promises = [];
let networkId = this.getNetworkId(network);
ipAddresses.forEach((aIpAddress) => {
let found = this.networkInterfaceLinks[networkId].extraRoutes.indexOf(aIpAddress);
if (found < 0) {
return; // continue
}
let promise =
this._setHostRoutes(false, [aIpAddress], network.name, network.getGateways())
.then(() => {
this.networkInterfaceLinks[networkId].extraRoutes.splice(found, 1);
}, () => {
// We should remove it even if the operation failed.
this.networkInterfaceLinks[networkId].extraRoutes.splice(found, 1);
});
promises.push(promise);
});
return Promise.all(promises);
});
},
isNetworkTypeSecondaryMobile: function(type) {
@ -547,16 +653,46 @@ NetworkManager.prototype = {
this.isNetworkTypeSecondaryMobile(type));
},
setHostRoutes: function(network) {
let hosts = network.getDnses().concat(network.httpProxyHost);
_handleGateways: function(networkId, gateways) {
let currentNetworkLinks = this.networkInterfaceLinks[networkId];
if (currentNetworkLinks.gateways.length == 0 ||
currentNetworkLinks.compareGateways(gateways)) {
return Promise.resolve();
}
return this._updateRoutes(true, hosts, network.name, network.getGateways());
let currentExtraRoutes = currentNetworkLinks.extraRoutes;
return this._cleanupAllHostRoutes(networkId)
.then(() => {
// If gateways have changed, re-add extra host routes with new gateways.
if (currentExtraRoutes.length > 0) {
this._setHostRoutes(true,
currentExtraRoutes,
currentNetworkLinks.interfaceName,
gateways)
.then(() => {
currentNetworkLinks.extraRoutes = currentExtraRoutes;
});
}
});
},
removeHostRoutes: function(network) {
let hosts = network.getDnses().concat(network.httpProxyHost);
_cleanupAllHostRoutes: function(networkId) {
let currentNetworkLinks = this.networkInterfaceLinks[networkId];
let hostRoutes = currentNetworkLinks.linkRoutes.concat(
currentNetworkLinks.extraRoutes);
return this._updateRoutes(false, hosts, network.name, network.getGateways());
if (hostRoutes.length === 0) {
return Promise.resolve();
}
return this._setHostRoutes(false,
hostRoutes,
currentNetworkLinks.interfaceName,
currentNetworkLinks.gateways)
.catch((aError) => {
debug("Error (" + aError + ") on _cleanupAllHostRoutes, keep proceeding.");
})
.then(() => currentNetworkLinks.resetLinks());
},
selectGateway: function(gateways, host) {

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

@ -56,6 +56,66 @@ function debug(msg) {
dump("-*- NetworkService: " + msg + "\n");
}
function Task(id, params, setupFunction) {
this.id = id;
this.params = params;
this.setupFunction = setupFunction;
}
function NetworkWorkerRequestQueue(networkService) {
this.networkService = networkService;
this.tasks = [];
}
NetworkWorkerRequestQueue.prototype = {
runQueue: function() {
if (this.tasks.length === 0) {
return;
}
let task = this.tasks[0];
if (DEBUG) debug("run task id: " + task.id);
if (typeof task.setupFunction === 'function') {
// If setupFunction returns false, skip sending to Network Worker but call
// handleWorkerMessage() directly with task id, as if the response was
// returned from Network Worker.
if (!task.setupFunction()) {
this.networkService.handleWorkerMessage({id: task.id});
return;
}
}
gNetworkWorker.postMessage(task.params);
},
enqueue: function(id, params, setupFunction) {
if (DEBUG) debug("enqueue id: " + id);
this.tasks.push(new Task(id, params, setupFunction));
if (this.tasks.length === 1) {
this.runQueue();
}
},
dequeue: function(id) {
if (DEBUG) debug("dequeue id: " + id);
if (!this.tasks.length || this.tasks[0].id != id) {
if (DEBUG) debug("Id " + id + " is not on top of the queue");
return;
}
this.tasks.shift();
if (this.tasks.length > 0) {
// Run queue on the next tick.
Services.tm.currentThread.dispatch(() => {
this.runQueue();
}, Ci.nsIThread.DISPATCH_NORMAL);
}
}
};
/**
* This component watches for network interfaces changing state and then
* adjusts routes etc. accordingly.
@ -76,6 +136,8 @@ function NetworkService() {
// Callbacks to invoke when a reply arrives from the net_worker.
this.controlCallbacks = Object.create(null);
this.addedRoutes = new Map();
this.netWorkerRequestQueue = new NetworkWorkerRequestQueue(this);
this.shutdown = false;
Services.obs.addObserver(this, "xpcom-shutdown", false);
}
@ -90,17 +152,26 @@ NetworkService.prototype = {
// Helpers
addedRoutes: null,
idgen: 0,
controlMessage: function(params, callback) {
controlMessage: function(params, callback, setupFunction) {
if (this.shutdown) {
return;
}
let id = this.idgen++;
params.id = id;
if (callback) {
let id = this.idgen++;
params.id = id;
this.controlCallbacks[id] = callback;
}
// For now, we use setupFunction to determine if this command needs to be
// queued or not.
if (setupFunction) {
this.netWorkerRequestQueue.enqueue(id, params, setupFunction);
return;
}
if (gNetworkWorker) {
gNetworkWorker.postMessage(params);
}
@ -118,6 +189,8 @@ NetworkService.prototype = {
callback.call(this, response);
delete this.controlCallbacks[id];
}
this.netWorkerRequestQueue.dequeue(id);
},
// nsINetworkService
@ -307,6 +380,10 @@ NetworkService.prototype = {
this.controlMessage(options);
},
_routeToString: function(interfaceName, host, prefixLength, gateway) {
return host + "-" + prefixLength + "-" + gateway + "-" + interfaceName;
},
modifyRoute: function(action, interfaceName, host, prefixLength, gateway) {
let command;
@ -322,8 +399,22 @@ NetworkService.prototype = {
return Promise.reject();
}
let route = this._routeToString(interfaceName, host, prefixLength, gateway);
let setupFunc = () => {
let count = this.addedRoutes.get(route);
if (DEBUG) debug(command + ": " + route + " -> " + count);
// Return false if there is no need to send the command to network worker.
if ((action == Ci.nsINetworkService.MODIFY_ROUTE_ADD && count) ||
(action == Ci.nsINetworkService.MODIFY_ROUTE_REMOVE &&
(!count || count > 1))) {
return false;
}
return true;
};
if (DEBUG) debug(command + " " + host + " on " + interfaceName);
let deferred = Promise.defer();
let options = {
cmd: command,
ifname: interfaceName,
@ -331,23 +422,32 @@ NetworkService.prototype = {
prefixLength: prefixLength,
ip: host
};
this.controlMessage(options, function(data) {
if (data.error) {
deferred.reject(data.reason);
return;
}
deferred.resolve();
});
return deferred.promise;
},
removeHostRoutes: function(ifname) {
if(DEBUG) debug("Going to remove all host routes on " + ifname);
let options = {
cmd: "removeHostRoutes",
ifname: ifname,
};
this.controlMessage(options);
return new Promise((aResolve, aReject) => {
this.controlMessage(options, (data) => {
let count = this.addedRoutes.get(route);
// Remove route from addedRoutes on success or failure.
if (action == Ci.nsINetworkService.MODIFY_ROUTE_REMOVE) {
if (count > 1) {
this.addedRoutes.set(route, count - 1);
} else {
this.addedRoutes.delete(route);
}
}
if (data.error) {
aReject(data.reason);
return;
}
if (action == Ci.nsINetworkService.MODIFY_ROUTE_ADD) {
this.addedRoutes.set(route, count ? count + 1 : 1);
}
aResolve();
}, setupFunc);
});
},
addSecondaryRoute: function(ifname, route) {

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

@ -1485,7 +1485,6 @@ void NetworkUtils::ExecuteCommand(NetworkParams aOptions)
BUILD_ENTRY(removeDefaultRoute),
BUILD_ENTRY(addHostRoute),
BUILD_ENTRY(removeHostRoute),
BUILD_ENTRY(removeHostRoutes),
BUILD_ENTRY(addSecondaryRoute),
BUILD_ENTRY(removeSecondaryRoute),
BUILD_ENTRY(setNetworkInterfaceAlarm),
@ -1955,6 +1954,11 @@ CommandResult NetworkUtils::addHostRoute(NetworkParams& aOptions)
*/
CommandResult NetworkUtils::addHostRouteLegacy(NetworkParams& aOptions)
{
if (aOptions.mGateway.IsEmpty()) {
ERROR("addHostRouteLegacy does not support empty gateway.");
return EINVAL;
}
NS_ConvertUTF16toUTF8 autoIfname(aOptions.mIfname);
NS_ConvertUTF16toUTF8 autoHostname(aOptions.mIp);
NS_ConvertUTF16toUTF8 autoGateway(aOptions.mGateway);
@ -2026,27 +2030,6 @@ CommandResult NetworkUtils::removeHostRouteLegacy(NetworkParams& aOptions)
prefix, autoGateway.get());
}
/**
* Remove the routes associated with the named interface.
*/
CommandResult NetworkUtils::removeHostRoutes(NetworkParams& aOptions)
{
if (SDK_VERSION < 20) {
return removeHostRoutesLegacy(aOptions);
}
NU_DBG("Don't know how to remove host routes on a interface");
return SUCCESS;
}
/**
* Remove the routes associated with the named interface.
*/
CommandResult NetworkUtils::removeHostRoutesLegacy(NetworkParams& aOptions)
{
return mNetUtils->do_ifc_remove_host_routes(GET_CHAR(mIfname));
}
CommandResult NetworkUtils::removeNetworkRoute(NetworkParams& aOptions)
{
if (SDK_VERSION < 20) {

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

@ -291,7 +291,6 @@ private:
CommandResult addHostRoute(NetworkParams& aOptions);
CommandResult removeDefaultRoute(NetworkParams& aOptions);
CommandResult removeHostRoute(NetworkParams& aOptions);
CommandResult removeHostRoutes(NetworkParams& aOptions);
CommandResult removeNetworkRoute(NetworkParams& aOptions);
CommandResult setDNS(NetworkParams& aOptions);
CommandResult addSecondaryRoute(NetworkParams& aOptions);
@ -310,7 +309,6 @@ private:
CommandResult addHostRouteLegacy(NetworkParams& aOptions);
CommandResult removeHostRouteLegacy(NetworkParams& aOptions);
CommandResult removeHostRoutesLegacy(NetworkParams& aOptions);
CommandResult setDefaultRouteLegacy(NetworkParams& aOptions);
CommandResult removeDefaultRouteLegacy(NetworkParams& aOptions);
CommandResult removeNetworkRouteLegacy(NetworkParams& aOptions);

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

@ -159,7 +159,7 @@ interface nsIDhcpRequestCallback : nsISupports
/**
* Provide network services.
*/
[scriptable, uuid(138ab267-0007-4c3e-8bfc-a7e1be0b5a6f)]
[scriptable, uuid(e40dd966-cb04-4dc7-ac4b-6382769c00b9)]
interface nsINetworkService : nsISupports
{
const long MODIFY_ROUTE_ADD = 0;
@ -345,14 +345,6 @@ interface nsINetworkService : nsISupports
in long prefixLength,
[optional] in DOMString gateway);
/**
* Remove all host routes.
*
* @param interfaceName
* The interface name we want remove from the routing table.
*/
void removeHostRoutes(in DOMString interfaceName);
/**
* Add route to secondary routing table.
*

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

@ -82,138 +82,6 @@ let RILQUIRKS_DATA_REGISTRATION_ON_DEMAND;
// Ril quirk to control the uicc/data subscription.
let RILQUIRKS_SUBSCRIPTION_CONTROL;
function BufObject(aContext) {
this.context = aContext;
}
BufObject.prototype = {
context: null,
mToken: 0,
mTokenRequestMap: null,
init: function() {
this._init();
// This gets incremented each time we send out a parcel.
this.mToken = 1;
// Maps tokens we send out with requests to the request type, so that
// when we get a response parcel back, we know what request it was for.
this.mTokenRequestMap = new Map();
},
/**
* Process one parcel.
*/
processParcel: function() {
let response_type = this.readInt32();
let request_type, options;
if (response_type == RESPONSE_TYPE_SOLICITED) {
let token = this.readInt32();
let error = this.readInt32();
options = this.mTokenRequestMap.get(token);
if (!options) {
if (DEBUG) {
this.context.debug("Suspicious uninvited request found: " +
token + ". Ignored!");
}
return;
}
this.mTokenRequestMap.delete(token);
request_type = options.rilRequestType;
options.rilRequestError = error;
if (DEBUG) {
this.context.debug("Solicited response for request type " + request_type +
", token " + token + ", error " + error);
}
} else if (response_type == RESPONSE_TYPE_UNSOLICITED) {
request_type = this.readInt32();
if (DEBUG) {
this.context.debug("Unsolicited response for request type " + request_type);
}
} else {
if (DEBUG) {
this.context.debug("Unknown response type: " + response_type);
}
return;
}
this.context.RIL.handleParcel(request_type, this.readAvailable, options);
},
/**
* Start a new outgoing parcel.
*
* @param type
* Integer specifying the request type.
* @param options [optional]
* Object containing information about the request, e.g. the
* original main thread message object that led to the RIL request.
*/
newParcel: function(type, options) {
if (DEBUG) {
this.context.debug("New outgoing parcel of type " + type +
", token " + this.mToken);
}
// We're going to leave room for the parcel size at the beginning.
this.outgoingIndex = this.PARCEL_SIZE_SIZE;
this.writeInt32(this._reMapRequestType(type));
this.writeInt32(this.mToken);
if (!options) {
options = {};
}
options.rilRequestType = type;
options.rilRequestError = null;
this.mTokenRequestMap.set(this.mToken, options);
this.mToken++;
return this.mToken;
},
simpleRequest: function(type, options) {
this.newParcel(type, options);
this.sendParcel();
},
onSendParcel: function(parcel) {
postRILMessage(this.context.clientId, parcel);
},
/**
* Remapping the request type to different values based on RIL version.
* We only have to do this for SUBSCRIPTION right now, so I just make it
* simple. A generic logic or structure could be discussed if we have more
* use cases, especially the cases from different partners.
*/
_reMapRequestType: function(type) {
let newType = type;
switch (type) {
case REQUEST_SET_UICC_SUBSCRIPTION:
case REQUEST_SET_DATA_SUBSCRIPTION:
if (this.context.RIL.version < 9) {
// Shift the CAF's proprietary parcels. Please see
// https://www.codeaurora.org/cgit/quic/la/platform/hardware/ril/tree/include/telephony/ril.h?h=b2g_jb_3.2
newType = type - 1;
}
break;
}
return newType;
}
};
(function() {
let base = require("resource://gre/modules/workers/worker_buf.js").Buf;
for (let p in base) {
BufObject.prototype[p] = base[p];
}
})();
const TELEPHONY_REQUESTS = [
REQUEST_GET_CURRENT_CALLS,
REQUEST_ANSWER,

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

@ -338,6 +338,15 @@ public:
return mLayer->GetClipRect();
}
bool GetForceDispatchToContentRegion() const {
MOZ_ASSERT(IsValid());
if (mLayer->AsContainerLayer()) {
return mLayer->AsContainerLayer()->GetForceDispatchToContentRegion();
}
return false;
}
// Expose an opaque pointer to the layer. Mostly used for printf
// purposes. This is not intended to be a general-purpose accessor
// for the underlying layer.

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

@ -924,7 +924,8 @@ ContainerLayer::ContainerLayer(LayerManager* aManager, void* aImplData)
mUseIntermediateSurface(false),
mSupportsComponentAlphaChildren(false),
mMayHaveReadbackChild(false),
mChildrenChanged(false)
mChildrenChanged(false),
mForceDispatchToContentRegion(false)
{
mContentFlags = 0; // Clear NO_TEXT, NO_TEXT_OVER_TRANSPARENT
}
@ -1081,6 +1082,7 @@ ContainerLayer::FillSpecificAttributes(SpecificLayerAttributes& aAttrs)
aAttrs = ContainerLayerAttributes(mPreXScale, mPreYScale,
mInheritedXScale, mInheritedYScale,
mPresShellResolution, mScaleToResolution,
mForceDispatchToContentRegion,
reinterpret_cast<uint64_t>(mHMDInfo.get()));
}
@ -1748,6 +1750,9 @@ ContainerLayer::PrintInfo(std::stringstream& aStream, const char* aPrefix)
if (mScaleToResolution) {
aStream << nsPrintfCString(" [presShellResolution=%g]", mPresShellResolution).get();
}
if (mForceDispatchToContentRegion) {
aStream << " [force-dtc]";
}
if (mHMDInfo) {
aStream << nsPrintfCString(" [hmd=%p]", mHMDInfo.get()).get();
}

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

@ -916,6 +916,10 @@ public:
* outside the dispatch-to-content region, we can initiate a gesture without
* consulting the content thread. Otherwise we must dispatch the event to
* content.
* Note that if a layer or any ancestor layer returns true for
* GetForceDispatchToContentRegion() then we must treat the dispatch-to-content
* region as encompassing the hit region, and therefore must consult the
* content thread before initiating a gesture.
*/
/**
* CONSTRUCTION PHASE ONLY
@ -1967,6 +1971,20 @@ public:
mChildrenChanged = aVal;
}
void SetForceDispatchToContentRegion(bool aVal) {
if (mForceDispatchToContentRegion == aVal) {
return;
}
MOZ_LAYERS_LOG_IF_SHADOWABLE(this, ("Layer::Mutated(%p) ForceDispatchToContentRegion", this));
mForceDispatchToContentRegion = aVal;
Mutated();
}
bool GetForceDispatchToContentRegion() const {
return mForceDispatchToContentRegion;
}
/**
* VR
*/
@ -2023,6 +2041,7 @@ protected:
// This is updated by ComputeDifferences. This will be true if we need to invalidate
// the intermediate surface.
bool mChildrenChanged;
bool mForceDispatchToContentRegion;
nsRefPtr<gfx::VRHMDInfo> mHMDInfo;
};

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

@ -320,6 +320,20 @@ APZCTreeManager::RecycleOrCreateNode(TreeBuildingState& aState,
return node.forget();
}
static bool
ShouldForceDispatchToContent(HitTestingTreeNode* aParent,
const LayerMetricsWrapper& aLayer)
{
// Make it so that if the flag is set on the layer tree, it automatically
// propagates to all the nodes in the corresponding subtree rooted at that
// layer in the hit-test tree. This saves having to walk up the tree every
// we want to see if a hit-test node is affected by this flag.
if (aParent && aParent->GetForceDispatchToContent()) {
return true;
}
return aLayer.GetForceDispatchToContentRegion();
}
HitTestingTreeNode*
APZCTreeManager::PrepareNodeForLayer(const LayerMetricsWrapper& aLayer,
const FrameMetrics& aMetrics,
@ -344,7 +358,8 @@ APZCTreeManager::PrepareNodeForLayer(const LayerMetricsWrapper& aLayer,
node = RecycleOrCreateNode(aState, nullptr);
AttachNodeToTree(node, aParent, aNextSibling);
node->SetHitTestData(GetEventRegions(aLayer), aLayer.GetTransform(),
aLayer.GetClipRect() ? Some(nsIntRegion(*aLayer.GetClipRect())) : Nothing());
aLayer.GetClipRect() ? Some(nsIntRegion(*aLayer.GetClipRect())) : Nothing(),
ShouldForceDispatchToContent(aParent, aLayer));
return node;
}
@ -440,7 +455,8 @@ APZCTreeManager::PrepareNodeForLayer(const LayerMetricsWrapper& aLayer,
MOZ_ASSERT(node->IsPrimaryHolder() && node->GetApzc() && node->GetApzc()->Matches(guid));
nsIntRegion clipRegion = ComputeClipRegion(state->mController, aLayer);
node->SetHitTestData(GetEventRegions(aLayer), aLayer.GetTransform(), Some(clipRegion));
node->SetHitTestData(GetEventRegions(aLayer), aLayer.GetTransform(), Some(clipRegion),
ShouldForceDispatchToContent(aParent, aLayer));
apzc->SetAncestorTransform(aAncestorTransform);
PrintAPZCInfo(aLayer, apzc);
@ -494,7 +510,8 @@ APZCTreeManager::PrepareNodeForLayer(const LayerMetricsWrapper& aLayer,
MOZ_ASSERT(aAncestorTransform == apzc->GetAncestorTransform());
nsIntRegion clipRegion = ComputeClipRegion(state->mController, aLayer);
node->SetHitTestData(GetEventRegions(aLayer), aLayer.GetTransform(), Some(clipRegion));
node->SetHitTestData(GetEventRegions(aLayer), aLayer.GetTransform(), Some(clipRegion),
ShouldForceDispatchToContent(aParent, aLayer));
}
return node;

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

@ -21,6 +21,7 @@ HitTestingTreeNode::HitTestingTreeNode(AsyncPanZoomController* aApzc,
bool aIsPrimaryHolder)
: mApzc(aApzc)
, mIsPrimaryApzcHolder(aIsPrimaryHolder)
, mForceDispatchToContent(false)
{
if (mIsPrimaryApzcHolder) {
MOZ_ASSERT(mApzc);
@ -155,11 +156,13 @@ HitTestingTreeNode::IsPrimaryHolder() const
void
HitTestingTreeNode::SetHitTestData(const EventRegions& aRegions,
const gfx::Matrix4x4& aTransform,
const Maybe<nsIntRegion>& aClipRegion)
const Maybe<nsIntRegion>& aClipRegion,
bool aForceDispatchToContent)
{
mEventRegions = aRegions;
mTransform = aTransform;
mClipRegion = aClipRegion;
mForceDispatchToContent = aForceDispatchToContent;
}
bool
@ -210,20 +213,29 @@ HitTestingTreeNode::HitTest(const ParentLayerPoint& aPoint) const
if (!mEventRegions.mHitRegion.Contains(point.x, point.y)) {
return HitTestResult::HitNothing;
}
if (mEventRegions.mDispatchToContentHitRegion.Contains(point.x, point.y)) {
if (mForceDispatchToContent ||
mEventRegions.mDispatchToContentHitRegion.Contains(point.x, point.y))
{
return HitTestResult::HitDispatchToContentRegion;
}
return HitTestResult::HitLayer;
}
bool
HitTestingTreeNode::GetForceDispatchToContent() const
{
return mForceDispatchToContent;
}
void
HitTestingTreeNode::Dump(const char* aPrefix) const
{
if (mPrevSibling) {
mPrevSibling->Dump(aPrefix);
}
printf_stderr("%sHitTestingTreeNode (%p) APZC (%p) g=(%s) r=(%s) t=(%s) c=(%s)\n",
printf_stderr("%sHitTestingTreeNode (%p) APZC (%p) g=(%s) %sr=(%s) t=(%s) c=(%s)\n",
aPrefix, this, mApzc.get(), mApzc ? Stringify(mApzc->GetGuid()).c_str() : "",
mForceDispatchToContent ? "fdtc " : "",
Stringify(mEventRegions).c_str(), Stringify(mTransform).c_str(),
mClipRegion ? Stringify(mClipRegion.ref()).c_str() : "none");
if (mLastChild) {

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

@ -81,7 +81,8 @@ public:
void SetHitTestData(const EventRegions& aRegions,
const gfx::Matrix4x4& aTransform,
const Maybe<nsIntRegion>& aClipRegion);
const Maybe<nsIntRegion>& aClipRegion,
bool aForceDispatchToContent);
bool IsOutsideClip(const ParentLayerPoint& aPoint) const;
/* Convert aPoint into the LayerPixel space for the layer corresponding to
* this node. */
@ -89,6 +90,8 @@ public:
/* Assuming aPoint is inside the clip region for this node, check which of the
* event region spaces it falls inside. */
HitTestResult HitTest(const ParentLayerPoint& aPoint) const;
/* Returns the mForceDispatchToContent flag. */
bool GetForceDispatchToContent() const;
/* Debug helpers */
void Dump(const char* aPrefix = "") const;
@ -122,6 +125,11 @@ private:
* because we may use the composition bounds of the layer if the clip is not
* present. This value is in L's ParentLayerPixels. */
Maybe<nsIntRegion> mClipRegion;
/* If this flag is set, then events to this node and the entire subtree under
* should always be treated as dispatch-to-content.
*/
bool mForceDispatchToContent;
};
}

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

@ -386,6 +386,7 @@ LayerTransactionParent::RecvUpdate(InfallibleTArray<Edit>&& cset,
containerLayer->SetInheritedScale(attrs.inheritedXScale(), attrs.inheritedYScale());
containerLayer->SetScaleToResolution(attrs.scaleToResolution(),
attrs.presShellResolution());
containerLayer->SetForceDispatchToContentRegion(attrs.forceDispatchToContentRegion());
if (attrs.hmdInfo()) {
if (!IsSameProcess()) {

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

@ -234,6 +234,7 @@ struct ContainerLayerAttributes {
float inheritedYScale;
float presShellResolution;
bool scaleToResolution;
bool forceDispatchToContentRegion;
// This is a bare pointer; LayerTransactionParent::RecvUpdate prevents this
// from being used when !IsSameProcess(), but we should make this truly
// cross process at some point by passing the HMDConfig

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

@ -587,8 +587,7 @@ nsDisplayListBuilder::nsDisplayListBuilder(nsIFrame* aReferenceFrame,
mIsPaintingToWindow(false),
mIsCompositingCheap(false),
mContainsPluginItem(false),
mAncestorHasTouchEventHandler(false),
mAncestorHasScrollEventHandler(false),
mAncestorHasApzAwareEventHandler(false),
mHaveScrollableDisplayPort(false),
mWindowDraggingAllowed(false),
mIsBuildingForPopup(nsLayoutUtils::IsPopup(aReferenceFrame))
@ -1630,6 +1629,9 @@ already_AddRefed<LayerManager> nsDisplayList::PaintRoot(nsDisplayListBuilder* aB
1.0f/containerParameters.mYScale);
root->SetScaleToResolution(presShell->ScaleToResolution(),
containerParameters.mXScale);
root->SetForceDispatchToContentRegion(
aBuilder->IsBuildingLayerEventRegions() &&
nsLayoutUtils::HasDocumentLevelListenersForApzAwareEvents(presShell));
if (gfxPrefs::LayoutUseContainersForRootFrames()) {
bool isRoot = presContext->IsRootContentDocument();
@ -3151,9 +3153,7 @@ nsDisplayLayerEventRegions::AddFrame(nsDisplayListBuilder* aBuilder,
} else {
mHitRegion.Or(mHitRegion, borderBox);
}
if (aBuilder->GetAncestorHasTouchEventHandler() ||
aBuilder->GetAncestorHasScrollEventHandler())
{
if (aBuilder->GetAncestorHasApzAwareEventHandler()) {
mDispatchToContentHitRegion.Or(mDispatchToContentHitRegion, borderBox);
}
}
@ -4064,6 +4064,9 @@ nsDisplaySubDocument::nsDisplaySubDocument(nsDisplayListBuilder* aBuilder,
, mScrollParentId(aBuilder->GetCurrentScrollParentId())
{
MOZ_COUNT_CTOR(nsDisplaySubDocument);
mForceDispatchToContentRegion =
aBuilder->IsBuildingLayerEventRegions() &&
nsLayoutUtils::HasDocumentLevelListenersForApzAwareEvents(aFrame->PresContext()->PresShell());
}
#ifdef NS_BUILD_REFCNT_LOGGING
@ -4085,7 +4088,9 @@ nsDisplaySubDocument::BuildLayer(nsDisplayListBuilder* aBuilder,
params.mInLowPrecisionDisplayPort = true;
}
return nsDisplayOwnLayer::BuildLayer(aBuilder, aManager, params);
nsRefPtr<Layer> layer = nsDisplayOwnLayer::BuildLayer(aBuilder, aManager, params);
layer->AsContainerLayer()->SetForceDispatchToContentRegion(mForceDispatchToContentRegion);
return layer.forget();
}
UniquePtr<FrameMetrics>

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

@ -345,15 +345,10 @@ public:
return CurrentPresShellState()->mInsidePointerEventsNoneDoc;
}
bool GetAncestorHasTouchEventHandler() { return mAncestorHasTouchEventHandler; }
void SetAncestorHasTouchEventHandler(bool aValue)
bool GetAncestorHasApzAwareEventHandler() { return mAncestorHasApzAwareEventHandler; }
void SetAncestorHasApzAwareEventHandler(bool aValue)
{
mAncestorHasTouchEventHandler = aValue;
}
bool GetAncestorHasScrollEventHandler() { return mAncestorHasScrollEventHandler; }
void SetAncestorHasScrollEventHandler(bool aValue)
{
mAncestorHasScrollEventHandler = aValue;
mAncestorHasApzAwareEventHandler = aValue;
}
bool HaveScrollableDisplayPort() const { return mHaveScrollableDisplayPort; }
@ -578,8 +573,7 @@ public:
mPrevOffset(aBuilder->mCurrentOffsetToReferenceFrame),
mPrevDirtyRect(aBuilder->mDirtyRect),
mPrevIsAtRootOfPseudoStackingContext(aBuilder->mIsAtRootOfPseudoStackingContext),
mPrevAncestorHasTouchEventHandler(aBuilder->mAncestorHasTouchEventHandler),
mPrevAncestorHasScrollEventHandler(aBuilder->mAncestorHasScrollEventHandler)
mPrevAncestorHasApzAwareEventHandler(aBuilder->mAncestorHasApzAwareEventHandler)
{
if (aForChild->IsTransformed()) {
aBuilder->mCurrentOffsetToReferenceFrame = nsPoint();
@ -624,8 +618,7 @@ public:
mBuilder->mCurrentOffsetToReferenceFrame = mPrevOffset;
mBuilder->mDirtyRect = mPrevDirtyRect;
mBuilder->mIsAtRootOfPseudoStackingContext = mPrevIsAtRootOfPseudoStackingContext;
mBuilder->mAncestorHasTouchEventHandler = mPrevAncestorHasTouchEventHandler;
mBuilder->mAncestorHasScrollEventHandler = mPrevAncestorHasScrollEventHandler;
mBuilder->mAncestorHasApzAwareEventHandler = mPrevAncestorHasApzAwareEventHandler;
mBuilder->mCurrentAnimatedGeometryRoot = mPrevAnimatedGeometryRoot;
}
private:
@ -637,8 +630,7 @@ public:
nsPoint mPrevOffset;
nsRect mPrevDirtyRect;
bool mPrevIsAtRootOfPseudoStackingContext;
bool mPrevAncestorHasTouchEventHandler;
bool mPrevAncestorHasScrollEventHandler;
bool mPrevAncestorHasApzAwareEventHandler;
};
/**
@ -920,8 +912,7 @@ private:
bool mIsPaintingToWindow;
bool mIsCompositingCheap;
bool mContainsPluginItem;
bool mAncestorHasTouchEventHandler;
bool mAncestorHasScrollEventHandler;
bool mAncestorHasApzAwareEventHandler;
// True when the first async-scrollable scroll frame for which we build a
// display list has a display port. An async-scrollable scroll frame is one
// which WantsAsyncScroll().
@ -3045,6 +3036,7 @@ public:
protected:
ViewID mScrollParentId;
bool mForceDispatchToContentRegion;
};
/**

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

@ -20,6 +20,7 @@
#include "nsIDOMHTMLElement.h"
#include "nsFrameList.h"
#include "nsGkAtoms.h"
#include "nsHtml5Atoms.h"
#include "nsIAtom.h"
#include "nsCSSPseudoElements.h"
#include "nsCSSAnonBoxes.h"
@ -7851,3 +7852,34 @@ nsLayoutUtils::SetBSizeFromFontMetrics(const nsIFrame* aFrame,
aFramePadding.BStart(aFrameWM));
aMetrics.BSize(aLineWM) += aFramePadding.BStartEnd(aFrameWM);
}
/* static */ bool
nsLayoutUtils::HasApzAwareListeners(EventListenerManager* aElm)
{
if (!aElm) {
return false;
}
return aElm->HasListenersFor(nsGkAtoms::ontouchstart) ||
aElm->HasListenersFor(nsGkAtoms::ontouchmove) ||
aElm->HasListenersFor(nsGkAtoms::onwheel) ||
aElm->HasListenersFor(nsGkAtoms::onDOMMouseScroll) ||
aElm->HasListenersFor(nsHtml5Atoms::onmousewheel);
}
/* static */ bool
nsLayoutUtils::HasDocumentLevelListenersForApzAwareEvents(nsIPresShell* aShell)
{
if (nsIDocument* doc = aShell->GetDocument()) {
WidgetEvent event(true, NS_EVENT_NULL);
nsTArray<EventTarget*> targets;
nsresult rv = EventDispatcher::Dispatch(doc, nullptr, &event, nullptr,
nullptr, nullptr, &targets);
NS_ENSURE_SUCCESS(rv, false);
for (size_t i = 0; i < targets.Length(); i++) {
if (HasApzAwareListeners(targets[i]->GetExistingListenerManager())) {
return true;
}
}
}
return false;
}

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

@ -63,6 +63,7 @@ struct nsStyleImageOrientation;
struct nsOverflowAreas;
namespace mozilla {
class EventListenerManager;
class SVGImageContext;
struct IntrinsicSize;
struct ContainerLayerParameters;
@ -2555,6 +2556,9 @@ public:
mozilla::WritingMode aLineWM,
mozilla::WritingMode aFrameWM);
static bool HasApzAwareListeners(mozilla::EventListenerManager* aElm);
static bool HasDocumentLevelListenersForApzAwareEvents(nsIPresShell* aShell);
private:
static uint32_t sFontSizeInflationEmPerLine;
static uint32_t sFontSizeInflationMinTwips;

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

@ -1909,18 +1909,8 @@ CheckForApzAwareEventHandlers(nsDisplayListBuilder* aBuilder, nsIFrame* aFrame)
return;
}
EventListenerManager* elm = nsContentUtils::GetExistingListenerManagerForNode(content);
if (!elm) {
return;
}
if (elm->HasListenersFor(nsGkAtoms::ontouchstart) ||
elm->HasListenersFor(nsGkAtoms::ontouchmove)) {
aBuilder->SetAncestorHasTouchEventHandler(true);
}
if (elm->HasListenersFor(nsGkAtoms::onwheel) ||
elm->HasListenersFor(nsGkAtoms::onDOMMouseScroll) ||
elm->HasListenersFor(nsHtml5Atoms::onmousewheel))
{
aBuilder->SetAncestorHasScrollEventHandler(true);
if (nsLayoutUtils::HasApzAwareListeners(elm)) {
aBuilder->SetAncestorHasApzAwareEventHandler(true);
}
}

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

@ -487,8 +487,7 @@ nsSubDocumentFrame::BuildDisplayList(nsDisplayListBuilder* aBuilder,
? nsLayoutUtils::FindOrCreateIDFor(rootScrollFrame->GetContent())
: aBuilder->GetCurrentScrollParentId());
aBuilder->SetAncestorHasTouchEventHandler(false);
aBuilder->SetAncestorHasScrollEventHandler(false);
aBuilder->SetAncestorHasApzAwareEventHandler(false);
subdocRootFrame->
BuildDisplayListForStackingContext(aBuilder, dirty, &childItems);
}

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

@ -615,6 +615,17 @@ RenderFrameParent::TakeFocusForClick()
} // namespace layout
} // namespace mozilla
nsDisplayRemote::nsDisplayRemote(nsDisplayListBuilder* aBuilder,
nsIFrame* aFrame,
RenderFrameParent* aRemoteFrame)
: nsDisplayItem(aBuilder, aFrame)
, mRemoteFrame(aRemoteFrame)
{
mForceDispatchToContentRegion =
aBuilder->IsBuildingLayerEventRegions() &&
nsLayoutUtils::HasDocumentLevelListenersForApzAwareEvents(aFrame->PresContext()->PresShell());
}
already_AddRefed<Layer>
nsDisplayRemote::BuildLayer(nsDisplayListBuilder* aBuilder,
LayerManager* aManager,
@ -624,6 +635,9 @@ nsDisplayRemote::BuildLayer(nsDisplayListBuilder* aBuilder,
nsIntRect visibleRect = GetVisibleRect().ToNearestPixels(appUnitsPerDevPixel);
visibleRect += aContainerParameters.mOffset;
nsRefPtr<Layer> layer = mRemoteFrame->BuildLayer(aBuilder, mFrame, aManager, visibleRect, this, aContainerParameters);
if (layer && layer->AsContainerLayer()) {
layer->AsContainerLayer()->SetForceDispatchToContentRegion(mForceDispatchToContentRegion);
}
return layer.forget();
}

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

@ -166,10 +166,7 @@ class nsDisplayRemote : public nsDisplayItem
public:
nsDisplayRemote(nsDisplayListBuilder* aBuilder, nsIFrame* aFrame,
RenderFrameParent* aRemoteFrame)
: nsDisplayItem(aBuilder, aFrame)
, mRemoteFrame(aRemoteFrame)
{}
RenderFrameParent* aRemoteFrame);
virtual LayerState GetLayerState(nsDisplayListBuilder* aBuilder,
LayerManager* aManager,
@ -187,6 +184,7 @@ public:
private:
RenderFrameParent* mRemoteFrame;
bool mForceDispatchToContentRegion;
};

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

@ -1698,7 +1698,7 @@ public class BrowserApp extends GeckoApp
Telemetry.addToHistogram("PLACES_BOOKMARKS_COUNT", db.getCount(cr, "bookmarks"));
Telemetry.addToHistogram("FENNEC_FAVICONS_COUNT", db.getCount(cr, "favicons"));
Telemetry.addToHistogram("FENNEC_THUMBNAILS_COUNT", db.getCount(cr, "thumbnails"));
Telemetry.addToHistogram("FENNEC_READING_LIST_COUNT", db.getCount(getContentResolver(), "readinglist"));
Telemetry.addToHistogram("FENNEC_READING_LIST_COUNT", db.getReadingListAccessor().getCount(cr));
Telemetry.addToHistogram("BROWSER_IS_USER_DEFAULT", (isDefaultBrowser(Intent.ACTION_VIEW) ? 1 : 0));
if (Versions.feature16Plus) {
Telemetry.addToHistogram("BROWSER_IS_ASSIST_DEFAULT", (isDefaultBrowser(Intent.ACTION_ASSIST) ? 1 : 0));

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

@ -398,11 +398,7 @@ public class BrowserLocaleManager implements LocaleManager {
*/
public static Collection<String> getPackagedLocaleTags(final Context context) {
final String resPath = "res/multilocale.json";
final String apkPath = context.getPackageResourcePath();
final String jarURL = "jar:jar:" + new File(apkPath).toURI() + "!/" +
AppConstants.OMNIJAR_NAME + "!/" +
resPath;
final String jarURL = GeckoJarReader.getJarURL(context, resPath);
final String contents = GeckoJarReader.getText(jarURL);
if (contents == null) {

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

@ -9,6 +9,7 @@ import org.json.JSONObject;
import org.mozilla.gecko.db.BrowserContract.ReadingListItems;
import org.mozilla.gecko.db.BrowserDB;
import org.mozilla.gecko.db.DBUtils;
import org.mozilla.gecko.db.ReadingListAccessor;
import org.mozilla.gecko.favicons.Favicons;
import org.mozilla.gecko.util.EventCallback;
import org.mozilla.gecko.util.NativeEventListener;
@ -30,18 +31,17 @@ public final class ReadingListHelper implements NativeEventListener {
protected final Context context;
private final BrowserDB db;
private final Uri readingListUriWithProfile;
private final ReadingListAccessor readingListAccessor;
private final ContentObserver contentObserver;
public ReadingListHelper(Context context, GeckoProfile profile) {
this.context = context;
this.db = profile.getDB();
this.readingListAccessor = db.getReadingListAccessor();
EventDispatcher.getInstance().registerGeckoThreadListener((NativeEventListener) this,
"Reader:AddToList", "Reader:UpdateList", "Reader:FaviconRequest", "Reader:ListStatusRequest", "Reader:RemoveFromList");
readingListUriWithProfile = DBUtils.appendProfile(profile.getName(), ReadingListItems.CONTENT_URI);
contentObserver = new ContentObserver(null) {
@Override
@ -50,7 +50,7 @@ public final class ReadingListHelper implements NativeEventListener {
}
};
context.getContentResolver().registerContentObserver(readingListUriWithProfile, false, contentObserver);
this.readingListAccessor.registerContentObserver(context, contentObserver);
}
public void uninit() {
@ -104,11 +104,11 @@ public final class ReadingListHelper implements NativeEventListener {
ThreadUtils.postToBackgroundThread(new Runnable() {
@Override
public void run() {
if (db.isReadingListItem(cr, url)) {
if (readingListAccessor.isReadingListItem(cr, url)) {
showToast(R.string.reading_list_duplicate, Toast.LENGTH_SHORT);
callback.sendError("URL already in reading list: " + url);
} else {
db.addReadingListItem(cr, values);
readingListAccessor.addReadingListItem(cr, values);
showToast(R.string.reading_list_added, Toast.LENGTH_SHORT);
callback.sendSuccess(url);
}
@ -126,7 +126,7 @@ public final class ReadingListHelper implements NativeEventListener {
ThreadUtils.postToBackgroundThread(new Runnable() {
@Override
public void run() {
db.updateReadingListItem(cr, values);
readingListAccessor.updateReadingListItem(cr, values);
}
});
}
@ -192,7 +192,7 @@ public final class ReadingListHelper implements NativeEventListener {
ThreadUtils.postToBackgroundThread(new Runnable() {
@Override
public void run() {
db.removeReadingListItemWithURL(context.getContentResolver(), url);
readingListAccessor.removeReadingListItemWithURL(context.getContentResolver(), url);
GeckoAppShell.sendEventToGecko(GeckoEvent.createBroadcastEvent("Reader:Removed", url));
showToast(R.string.page_removed, Toast.LENGTH_SHORT);
}
@ -207,7 +207,7 @@ public final class ReadingListHelper implements NativeEventListener {
ThreadUtils.postToBackgroundThread(new Runnable() {
@Override
public void run() {
final int inReadingList = db.isReadingListItem(context.getContentResolver(), url) ? 1 : 0;
final int inReadingList = readingListAccessor.isReadingListItem(context.getContentResolver(), url) ? 1 : 0;
final JSONObject json = new JSONObject();
try {
@ -239,7 +239,7 @@ public final class ReadingListHelper implements NativeEventListener {
ThreadUtils.postToBackgroundThread(new Runnable() {
@Override
public void run() {
final Cursor c = db.getReadingListUnfetched(context.getContentResolver());
final Cursor c = readingListAccessor.getReadingListUnfetched(context.getContentResolver());
try {
while (c.moveToNext()) {
JSONObject json = new JSONObject();

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

@ -16,7 +16,6 @@ import org.mozilla.gecko.favicons.decoders.LoadFaviconResult;
import android.content.ContentProviderOperation;
import android.content.ContentResolver;
import android.content.ContentValues;
import android.content.Context;
import android.database.ContentObserver;
import android.database.Cursor;
@ -43,6 +42,7 @@ public interface BrowserDB {
public abstract Searches getSearches();
public abstract TabsAccessor getTabsAccessor();
public abstract URLMetadata getURLMetadata();
public abstract ReadingListAccessor getReadingListAccessor();
/**
* Add default bookmarks to the database.
@ -118,17 +118,6 @@ public interface BrowserDB {
*/
public abstract Cursor getBookmarksInFolder(ContentResolver cr, long folderId);
/**
* Can return <code>null</code>.
*/
public abstract Cursor getReadingList(ContentResolver cr);
public abstract Cursor getReadingListUnfetched(ContentResolver cr);
public abstract boolean isReadingListItem(ContentResolver cr, String uri);
public abstract void addReadingListItem(ContentResolver cr, ContentValues values);
public abstract void updateReadingListItem(ContentResolver cr, ContentValues values);
public abstract void removeReadingListItemWithURL(ContentResolver cr, String uri);
/**
* Get the favicon from the database, if any, associated with the given favicon URL. (That is,
* the URL of the actual favicon image, not the URL of the page with which the favicon is associated.)

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

@ -98,11 +98,11 @@ public class LocalBrowserDB implements BrowserDB {
private final Uri mUpdateHistoryUriWithProfile;
private final Uri mFaviconsUriWithProfile;
private final Uri mThumbnailsUriWithProfile;
private final Uri mReadingListUriWithProfile;
private LocalSearches searches;
private LocalTabsAccessor tabsAccessor;
private LocalURLMetadata urlMetadata;
private LocalReadingListAccessor readingListAccessor;
private static final String[] DEFAULT_BOOKMARK_COLUMNS =
new String[] { Bookmarks._ID,
@ -123,7 +123,6 @@ public class LocalBrowserDB implements BrowserDB {
mCombinedUriWithProfile = DBUtils.appendProfile(profile, Combined.CONTENT_URI);
mFaviconsUriWithProfile = DBUtils.appendProfile(profile, Favicons.CONTENT_URI);
mThumbnailsUriWithProfile = DBUtils.appendProfile(profile, Thumbnails.CONTENT_URI);
mReadingListUriWithProfile = DBUtils.appendProfile(profile, ReadingListItems.CONTENT_URI);
mUpdateHistoryUriWithProfile =
mHistoryUriWithProfile.buildUpon()
@ -134,6 +133,7 @@ public class LocalBrowserDB implements BrowserDB {
searches = new LocalSearches(mProfile);
tabsAccessor = new LocalTabsAccessor(mProfile);
urlMetadata = new LocalURLMetadata(mProfile);
readingListAccessor = new LocalReadingListAccessor(mProfile);
}
@Override
@ -151,6 +151,11 @@ public class LocalBrowserDB implements BrowserDB {
return urlMetadata;
}
@Override
public ReadingListAccessor getReadingListAccessor() {
return readingListAccessor;
}
/**
* Not thread safe. A helper to allocate new IDs for arbitrary strings.
*/
@ -468,18 +473,13 @@ public class LocalBrowserDB implements BrowserDB {
* compatible with the favicon decoder (most probably a PNG or ICO file).
*/
private static ConsumedInputStream getDefaultFaviconFromPath(Context context, String name) {
int faviconId = getFaviconId(name);
final int faviconId = getFaviconId(name);
if (faviconId == FAVICON_ID_NOT_FOUND) {
return null;
}
String path = context.getString(faviconId);
String apkPath = context.getPackageResourcePath();
File apkFile = new File(apkPath);
String bitmapPath = "jar:jar:" + apkFile.toURI() + "!/" + AppConstants.OMNIJAR_NAME + "!/" + path;
InputStream iStream = GeckoJarReader.getStream(bitmapPath);
final String bitmapPath = GeckoJarReader.getJarURL(context, context.getString(faviconId));
final InputStream iStream = GeckoJarReader.getStream(bitmapPath);
return IOUtils.readFully(iStream, DEFAULT_FAVICON_BUFFER_SIZE);
}
@ -581,9 +581,6 @@ public class LocalBrowserDB implements BrowserDB {
} else if ("favicons".equals(database)) {
uri = mFaviconsUriWithProfile;
columns = new String[] { Favicons._ID };
} else if ("readinglist".equals(database)) {
uri = mReadingListUriWithProfile;
columns = new String[] { ReadingListItems._ID };
}
if (uri != null) {
@ -824,26 +821,6 @@ public class LocalBrowserDB implements BrowserDB {
}
}
@Override
public boolean isReadingListItem(ContentResolver cr, String uri) {
final Cursor c = cr.query(mReadingListUriWithProfile,
new String[] { ReadingListItems._ID },
ReadingListItems.URL + " = ? ",
new String[] { uri },
null);
if (c == null) {
Log.e(LOGTAG, "Null cursor in isReadingListItem");
return false;
}
try {
return c.getCount() > 0;
} finally {
c.close();
}
}
@Override
public String getUrlForKeyword(ContentResolver cr, String keyword) {
final Cursor c = cr.query(mBookmarksUriWithProfile,
@ -973,70 +950,6 @@ public class LocalBrowserDB implements BrowserDB {
cr.delete(contentUri, urlEquals, urlArgs);
}
@Override
public Cursor getReadingList(ContentResolver cr) {
return cr.query(mReadingListUriWithProfile,
ReadingListItems.DEFAULT_PROJECTION,
null,
null,
null);
}
@Override
public Cursor getReadingListUnfetched(ContentResolver cr) {
return cr.query(mReadingListUriWithProfile,
new String[] { ReadingListItems._ID, ReadingListItems.URL },
ReadingListItems.CONTENT_STATUS + " = " + ReadingListItems.STATUS_UNFETCHED,
null,
null);
}
@Override
public void addReadingListItem(ContentResolver cr, ContentValues values) {
// Check that required fields are present.
for (String field: ReadingListItems.REQUIRED_FIELDS) {
if (!values.containsKey(field)) {
throw new IllegalArgumentException("Missing required field for reading list item: " + field);
}
}
// Clear delete flag if necessary
values.put(ReadingListItems.IS_DELETED, 0);
// Restore deleted record if possible
final Uri insertUri = mReadingListUriWithProfile
.buildUpon()
.appendQueryParameter(BrowserContract.PARAM_INSERT_IF_NEEDED, "true")
.build();
final int updated = cr.update(insertUri,
values,
ReadingListItems.URL + " = ? ",
new String[] { values.getAsString(ReadingListItems.URL) });
debug("Updated " + updated + " rows to new modified time.");
}
@Override
public void updateReadingListItem(ContentResolver cr, ContentValues values) {
if (!values.containsKey(ReadingListItems._ID)) {
throw new IllegalArgumentException("Cannot update reading list item without an ID");
}
final int updated = cr.update(mReadingListUriWithProfile,
values,
ReadingListItems._ID + " = ? ",
new String[] { values.getAsString(ReadingListItems._ID) });
debug("Updated " + updated + " reading list rows.");
}
@Override
public void removeReadingListItemWithURL(ContentResolver cr, String uri) {
cr.delete(mReadingListUriWithProfile, ReadingListItems.URL + " = ? ", new String[] { uri });
}
@Override
public void registerBookmarkObserver(ContentResolver cr, ContentObserver observer) {
cr.registerContentObserver(mBookmarksUriWithProfile, false, observer);

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

@ -0,0 +1,128 @@
/* 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/. */
package org.mozilla.gecko.db;
import android.content.ContentResolver;
import android.content.ContentValues;
import android.content.Context;
import android.database.ContentObserver;
import android.database.Cursor;
import android.net.Uri;
import android.util.Log;
public class LocalReadingListAccessor implements ReadingListAccessor {
private static final String LOG_TAG = "GeckoReadingListAcc";
private final Uri mReadingListUriWithProfile;
public LocalReadingListAccessor(final String profile) {
mReadingListUriWithProfile = DBUtils.appendProfile(profile, BrowserContract.ReadingListItems.CONTENT_URI);
}
@Override
public int getCount(ContentResolver cr) {
final String[] columns = new String[]{BrowserContract.ReadingListItems._ID};
final Cursor cursor = cr.query(mReadingListUriWithProfile, columns, null, null, null);
int count = 0;
try {
count = cursor.getCount();
} finally {
cursor.close();
}
Log.d(LOG_TAG, "Got count " + count + " for reading list.");
return count;
}
@Override
public Cursor getReadingList(ContentResolver cr) {
return cr.query(mReadingListUriWithProfile,
BrowserContract.ReadingListItems.DEFAULT_PROJECTION,
null,
null,
null);
}
@Override
public Cursor getReadingListUnfetched(ContentResolver cr) {
return cr.query(mReadingListUriWithProfile,
new String[] { BrowserContract.ReadingListItems._ID, BrowserContract.ReadingListItems.URL },
BrowserContract.ReadingListItems.CONTENT_STATUS + " = " + BrowserContract.ReadingListItems.STATUS_UNFETCHED,
null,
null);
}
@Override
public boolean isReadingListItem(ContentResolver cr, String uri) {
final Cursor c = cr.query(mReadingListUriWithProfile,
new String[] { BrowserContract.ReadingListItems._ID },
BrowserContract.ReadingListItems.URL + " = ? ",
new String[] { uri },
null);
if (c == null) {
Log.e(LOG_TAG, "Null cursor in isReadingListItem");
return false;
}
try {
return c.getCount() > 0;
} finally {
c.close();
}
}
@Override
public void addReadingListItem(ContentResolver cr, ContentValues values) {
// Check that required fields are present.
for (String field: BrowserContract.ReadingListItems.REQUIRED_FIELDS) {
if (!values.containsKey(field)) {
throw new IllegalArgumentException("Missing required field for reading list item: " + field);
}
}
// Clear delete flag if necessary
values.put(BrowserContract.ReadingListItems.IS_DELETED, 0);
// Restore deleted record if possible
final Uri insertUri = mReadingListUriWithProfile
.buildUpon()
.appendQueryParameter(BrowserContract.PARAM_INSERT_IF_NEEDED, "true")
.build();
final int updated = cr.update(insertUri,
values,
BrowserContract.ReadingListItems.URL + " = ? ",
new String[] { values.getAsString(BrowserContract.ReadingListItems.URL) });
Log.d(LOG_TAG, "Updated " + updated + " rows to new modified time.");
}
@Override
public void updateReadingListItem(ContentResolver cr, ContentValues values) {
if (!values.containsKey(BrowserContract.ReadingListItems._ID)) {
throw new IllegalArgumentException("Cannot update reading list item without an ID");
}
final int updated = cr.update(mReadingListUriWithProfile,
values,
BrowserContract.ReadingListItems._ID + " = ? ",
new String[] { values.getAsString(BrowserContract.ReadingListItems._ID) });
Log.d(LOG_TAG, "Updated " + updated + " reading list rows.");
}
@Override
public void removeReadingListItemWithURL(ContentResolver cr, String uri) {
cr.delete(mReadingListUriWithProfile, BrowserContract.ReadingListItems.URL + " = ? ", new String[]{uri});
}
@Override
public void registerContentObserver(Context context, ContentObserver observer) {
context.getContentResolver().registerContentObserver(mReadingListUriWithProfile, false, observer);
}
}

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

@ -0,0 +1,32 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
package org.mozilla.gecko.db;
import android.content.ContentResolver;
import android.content.ContentValues;
import android.content.Context;
import android.database.ContentObserver;
import android.database.Cursor;
public interface ReadingListAccessor {
/**
* Can return <code>null</code>.
*/
Cursor getReadingList(ContentResolver cr);
int getCount(ContentResolver cr);
Cursor getReadingListUnfetched(ContentResolver cr);
boolean isReadingListItem(ContentResolver cr, String uri);
void addReadingListItem(ContentResolver cr, ContentValues values);
void updateReadingListItem(ContentResolver cr, ContentValues values);
void removeReadingListItemWithURL(ContentResolver cr, String uri);
void registerContentObserver(Context context, ContentObserver observer);
}

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

@ -26,6 +26,48 @@ import android.database.ContentObserver;
import android.database.Cursor;
import android.graphics.drawable.BitmapDrawable;
class StubReadingListAccessor implements ReadingListAccessor {
@Override
public Cursor getReadingList(ContentResolver cr) {
return null;
}
@Override
public int getCount(ContentResolver cr) {
return 0;
}
@Override
public Cursor getReadingListUnfetched(ContentResolver cr) {
return null;
}
@Override
public boolean isReadingListItem(ContentResolver cr, String uri) {
return false;
}
@Override
public void addReadingListItem(ContentResolver cr, ContentValues values) {
}
@Override
public void updateReadingListItem(ContentResolver cr, ContentValues values) {
}
@Override
public void removeReadingListItemWithURL(ContentResolver cr, String uri) {
}
@Override
public void registerContentObserver(Context context, ContentObserver observer) {
}
}
class StubSearches implements Searches {
public StubSearches() {
}
@ -91,6 +133,7 @@ public class StubBrowserDB implements BrowserDB {
private final StubSearches searches = new StubSearches();
private final StubTabsAccessor tabsAccessor = new StubTabsAccessor();
private final StubURLMetadata urlMetadata = new StubURLMetadata();
private final StubReadingListAccessor readingListAccessor = new StubReadingListAccessor();
@Override
public Searches getSearches() {
@ -107,6 +150,11 @@ public class StubBrowserDB implements BrowserDB {
return urlMetadata;
}
@Override
public ReadingListAccessor getReadingListAccessor() {
return readingListAccessor;
}
protected static final Integer FAVICON_ID_NOT_FOUND = Integer.MIN_VALUE;
public StubBrowserDB(String profile) {

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

@ -498,10 +498,7 @@ public class Favicons {
* "jar:jar:file:///data/app/org.mozilla.firefox-1.apk!/assets/omni.ja!/chrome/chrome/content/branding/favicon64.png"
*/
private static String getBrandingBitmapPath(Context context, String name) {
final String apkPath = context.getPackageResourcePath();
return "jar:jar:" + new File(apkPath).toURI() + "!/" +
AppConstants.OMNIJAR_NAME + "!/" +
"chrome/chrome/content/branding/" + name;
return GeckoJarReader.getJarURL(context, "chrome/chrome/content/branding/" + name);
}
private static Bitmap loadBrandingBitmap(Context context, String name) {

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

@ -363,7 +363,7 @@ public abstract class HomeFragment extends Fragment {
break;
case READING_LIST:
mDB.removeReadingListItemWithURL(cr, mUrl);
mDB.getReadingListAccessor().removeReadingListItemWithURL(cr, mUrl);
GeckoAppShell.sendEventToGecko(GeckoEvent.createBroadcastEvent("Reader:Removed", mUrl));
break;

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

@ -15,6 +15,7 @@ import org.mozilla.gecko.TelemetryContract;
import org.mozilla.gecko.db.BrowserContract.ReadingListItems;
import org.mozilla.gecko.db.BrowserContract.URLColumns;
import org.mozilla.gecko.db.BrowserDB;
import org.mozilla.gecko.db.ReadingListAccessor;
import org.mozilla.gecko.home.HomeContextMenuInfo.RemoveItemType;
import org.mozilla.gecko.home.HomePager.OnUrlOpenListener;
@ -164,16 +165,16 @@ public class ReadingListPanel extends HomeFragment {
* Cursor loader for the list of reading list items.
*/
private static class ReadingListLoader extends SimpleCursorLoader {
private final BrowserDB mDB;
private final ReadingListAccessor accessor;
public ReadingListLoader(Context context) {
super(context);
mDB = GeckoProfile.get(context).getDB();
accessor = GeckoProfile.get(context).getDB().getReadingListAccessor();
}
@Override
public Cursor loadCursor() {
return mDB.getReadingList(getContext().getContentResolver());
return accessor.getReadingList(getContext().getContentResolver());
}
}

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

@ -159,12 +159,14 @@ gbjar.sources += [
'db/FormHistoryProvider.java',
'db/HomeProvider.java',
'db/LocalBrowserDB.java',
'db/LocalReadingListAccessor.java',
'db/LocalSearches.java',
'db/LocalTabsAccessor.java',
'db/LocalURLMetadata.java',
'db/PasswordsProvider.java',
'db/PerProfileDatabaseProvider.java',
'db/PerProfileDatabases.java',
'db/ReadingListAccessor.java',
'db/ReadingListProvider.java',
'db/RemoteClient.java',
'db/RemoteTab.java',

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

@ -34,7 +34,7 @@ public class AddToReadingList extends ShareMethod {
values.put(Bookmarks.TITLE, shareData.title);
values.put(Bookmarks.URL, shareData.url);
browserDB.addReadingListItem(resolver, values);
browserDB.getReadingListAccessor().addReadingListItem(resolver, values);
return Result.SUCCESS;
}

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

@ -261,7 +261,7 @@ public class ShareDialog extends Locales.LocaleAwareActivity implements SendTabT
final ContentResolver contentResolver = getApplicationContext().getContentResolver();
isBookmark = browserDB.isBookmark(contentResolver, pageURL);
isReadingListItem = browserDB.isReadingListItem(contentResolver, pageURL);
isReadingListItem = browserDB.getReadingListAccessor().isReadingListItem(contentResolver, pageURL);
return null;
}

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

@ -14,6 +14,13 @@ import org.mozilla.gecko.util.GeckoJarReader;
* as loading some invalid jar urls.
*/
public class testJarReader extends BaseTest {
public void testGetJarURL() {
// Invalid characters are escaped.
final String s = GeckoJarReader.computeJarURI("some[1].apk", "something/else");
mAsserter.ok(!s.contains("["), "Illegal characters are escaped away.", null);
mAsserter.ok(!s.toLowerCase().contains("%2f"), "Path characters aren't escaped.", null);
}
public void testJarReader() {
String appPath = getActivity().getApplication().getPackageResourcePath();
mAsserter.isnot(appPath, null, "getPackageResourcePath is non-null");

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

@ -4,6 +4,8 @@
package org.mozilla.gecko.util;
import android.content.Context;
import org.mozilla.gecko.AppConstants;
import org.mozilla.gecko.mozglue.NativeZip;
import android.content.res.Resources;
@ -13,6 +15,7 @@ import android.util.Log;
import org.mozilla.gecko.mozglue.RobocopTarget;
import java.io.BufferedReader;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
@ -171,4 +174,23 @@ public final class GeckoJarReader {
return results;
}
}
public static String getJarURL(Context context, String pathInsideJAR) {
// We need to encode the package resource path, because it might contain illegal characters. For example:
// /mnt/asec2/[2]org.mozilla.fennec-1/pkg.apk
// The round-trip through a URI does this for us.
final String resourcePath = context.getPackageResourcePath();
return computeJarURI(resourcePath, pathInsideJAR);
}
/**
* Encodes its resource path correctly.
*/
@RobocopTarget
public static String computeJarURI(String resourcePath, String pathInsideJAR) {
final String resURI = new File(resourcePath).toURI().toString();
// TODO: do we need to encode the file path, too?
return "jar:jar:" + resURI + "!/" + AppConstants.OMNIJAR_NAME + "!/" + pathInsideJAR;
}
}

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

@ -8,7 +8,6 @@ import android.content.Context;
import android.content.SharedPreferences;
import android.text.TextUtils;
import android.util.Log;
import org.json.JSONException;
import org.json.JSONObject;
import org.mozilla.gecko.AppConstants;
@ -16,20 +15,18 @@ import org.mozilla.gecko.GeckoProfile;
import org.mozilla.gecko.GeckoSharedPrefs;
import org.mozilla.gecko.Locales;
import org.mozilla.gecko.R;
import org.mozilla.gecko.distribution.Distribution;
import org.mozilla.gecko.util.FileUtils;
import org.mozilla.gecko.util.GeckoJarReader;
import org.mozilla.gecko.util.HardwareUtils;
import org.mozilla.gecko.util.RawResource;
import org.mozilla.gecko.util.ThreadUtils;
import org.mozilla.gecko.distribution.Distribution;
import org.mozilla.search.Constants;
import org.xmlpull.v1.XmlPullParserException;
import java.io.BufferedInputStream;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
@ -37,8 +34,6 @@ import java.io.OutputStream;
import java.io.UnsupportedEncodingException;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
public class SearchEngineManager implements SharedPreferences.OnSharedPreferenceChangeListener {
@ -532,7 +527,7 @@ public class SearchEngineManager implements SharedPreferences.OnSharedPreference
return engine;
}
} catch (IOException e) {
Log.e(LOG_TAG, "Error creating earch engine from name: " + name, e);
Log.e(LOG_TAG, "Error creating search engine from name: " + name, e);
}
}
return null;
@ -573,7 +568,7 @@ public class SearchEngineManager implements SharedPreferences.OnSharedPreference
// First, try a file path for the full locale.
final String languageTag = Locales.getLanguageTag(locale);
String url = getSearchPluginsJarURL(languageTag, fileName);
String url = getSearchPluginsJarURL(context, languageTag, fileName);
InputStream in = GeckoJarReader.getStream(url);
if (in != null) {
@ -583,7 +578,7 @@ public class SearchEngineManager implements SharedPreferences.OnSharedPreference
// If that doesn't work, try a file path for just the language.
final String language = Locales.getLanguage(locale);
if (!languageTag.equals(language)) {
url = getSearchPluginsJarURL(language, fileName);
url = getSearchPluginsJarURL(context, language, fileName);
in = GeckoJarReader.getStream(url);
if (in != null) {
return in;
@ -591,7 +586,7 @@ public class SearchEngineManager implements SharedPreferences.OnSharedPreference
}
// Finally, fall back to default locale defined in chrome registry.
url = getSearchPluginsJarURL(getFallbackLocale(), fileName);
url = getSearchPluginsJarURL(context, getFallbackLocale(), fileName);
return GeckoJarReader.getStream(url);
}
@ -606,7 +601,7 @@ public class SearchEngineManager implements SharedPreferences.OnSharedPreference
return fallbackLocale;
}
final InputStream in = GeckoJarReader.getStream(getJarURL("!/chrome/chrome.manifest"));
final InputStream in = GeckoJarReader.getStream(GeckoJarReader.getJarURL(context, "chrome/chrome.manifest"));
final BufferedReader br = getBufferedReader(in);
try {
@ -638,13 +633,9 @@ public class SearchEngineManager implements SharedPreferences.OnSharedPreference
* @param fileName The name of the file to read.
* @return URL for jar file.
*/
private String getSearchPluginsJarURL(String locale, String fileName) {
final String path = "!/chrome/" + locale + "/locale/" + locale + "/browser/searchplugins/" + fileName;
return getJarURL(path);
}
private String getJarURL(String path) {
return "jar:jar:file://" + context.getPackageResourcePath() + "!/" + AppConstants.OMNIJAR_NAME + path;
private static String getSearchPluginsJarURL(Context context, String locale, String fileName) {
final String path = "chrome/" + locale + "/locale/" + locale + "/browser/searchplugins/" + fileName;
return GeckoJarReader.getJarURL(context, path);
}
private BufferedReader getBufferedReader(InputStream in) {

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

@ -1,4 +1,4 @@
FROM quay.io/mozilla/builder:0.0.29
FROM quay.io/mozilla/builder:0.2.6
MAINTAINER Wander Lairson Costa <wcosta@mozilla.com>
# Add utilities and configuration

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

@ -1 +1 @@
0.0.7
0.0.8

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

@ -29,6 +29,8 @@ if [ ! -d $OBJDIR ]; then
mkdir -p $OBJDIR
fi
# Figure out where the remote manifest is so we can use caches for it.
MANIFEST=$(repository-url.py $GECKO_HEAD_REPOSITORY $GECKO_HEAD_REV b2g/config/$TARGET/sources.xml)
tc-vcs repo-checkout $OBJDIR/B2G https://git.mozilla.org/b2g/B2G.git $MANIFEST
debug_flag=""

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

@ -315,6 +315,10 @@ class Graph(object):
test_parameters['total_chunks'] = test['chunks']
for chunk in range(1, test_parameters['total_chunks'] + 1):
if 'only_chunks' in test and \
chunk not in test['only_chunks']:
continue;
test_parameters['chunk'] = chunk
test_task = templates.load(test['task'], test_parameters)
test_task['taskId'] = slugid()

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

@ -3,13 +3,15 @@
# You can obtain one at http://mozilla.org/MPL/2.0/.
import shlex
import argparse
import functools
import copy
import functools
import re
import shlex
from try_test_parser import parse_test_opts
TRY_DELIMITER='try:'
TRY_DELIMITER = 'try:'
TEST_CHUNK_SUFFIX = re.compile('(.*)-([0-9]+)$')
# The build type aliases are very cryptic and only used in try flags these are
# mappings from the single char alias to a longer more recognizable form.
@ -59,9 +61,41 @@ def normalize_test_list(all_tests, job_list):
if 'platforms' in all_entry:
entry['platforms'] = list(all_entry['platforms'])
results.append(entry)
return results
return parse_test_chunks(results)
else:
return tests
return parse_test_chunks(tests)
def parse_test_chunks(tests):
'''
Test flags may include parameters to narrow down the number of chunks in a
given push. We don't model 1 chunk = 1 job in taskcluster so we must check
each test flag to see if it is actually specifying a chunk.
:param list tests: Result from normalize_test_list
:returns: List of jobs
'''
results = []
seen_chunks = {}
for test in tests:
matches = TEST_CHUNK_SUFFIX.match(test['test'])
if not matches:
results.append(test)
continue
name = matches.group(1)
chunk = int(matches.group(2))
if name in seen_chunks:
seen_chunks[name].add(chunk)
else:
seen_chunks[name] = set([chunk])
test['test'] = name
test['only_chunks'] = seen_chunks[name]
results.append(test)
return results;
def extract_tests_from_platform(test_jobs, build_platform, build_task, tests):
'''
@ -104,7 +138,18 @@ def extract_tests_from_platform(test_jobs, build_platform, build_task, tests):
# Add the job to the list and ensure to copy it so we don't accidentally
# mutate the state of the test job in the future...
results.append(copy.deepcopy(test_job))
specific_test_job = copy.deepcopy(test_job)
# Update the task configuration for all tests in the matrix...
for build_name in specific_test_job:
for test_task_name in specific_test_job[build_name]:
test_task = specific_test_job[build_name][test_task_name]
# Copy over the chunk restrictions if given...
if 'only_chunks' in test_entry:
test_task['only_chunks'] = \
copy.copy(test_entry['only_chunks'])
results.append(specific_test_job)
return results

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

@ -343,6 +343,62 @@ class TestCommitParser(unittest.TestCase):
result = parse_commit(commit, jobs)
self.assertEqual(expected, result)
def test_specific_chunks(self):
'''
This test covers specifying specific chunks for a given test suite.
'''
commit = 'try: -b o -p linux -u mochitest-1,mochitest-2 -t none'
jobs = {
'flags': {
'builds': ['linux'],
'tests': ['mochitest'],
},
'builds': {
'linux': {
'types': {
'opt': {
'task': 'task/linux',
},
'debug': {
'task': 'task/linux-debug'
}
}
},
},
'tests': {
'mochitest': {
'allowed_build_tasks': {
'task/linux': {
'task': 'task/mochitest',
'chunks': 5
},
}
}
}
}
expected = [
{
'task': 'task/linux',
'dependents': [
{
'allowed_build_tasks': {
'task/linux': {
'task': 'task/mochitest',
'chunks': 5,
'only_chunks': set([1, 2])
},
}
}
],
'additional-parameters': {}
}
]
result = parse_commit(commit, jobs)
self.assertEqual(expected, result)
def test_commit_with_builds_and_tests(self):
'''
This test covers the broad case of a commit which has both builds and

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

@ -950,9 +950,12 @@ nsBaseWidget::DispatchEventForAPZ(WidgetGUIEvent* aEvent,
= &APZCTreeManager::SetTargetAPZC;
APZThreadUtils::RunOnControllerThread(NewRunnableMethod(
mAPZC.get(), setTargetApzcFunc, aInputBlockId, aGuid));
bool defaultPrevented = aEvent->AsTouchEvent()
? (nsIPresShell::gPreventMouseEvents || aEvent->mFlags.mMultipleActionsPrevented)
: aEvent->mFlags.mDefaultPrevented;
APZThreadUtils::RunOnControllerThread(NewRunnableMethod(
mAPZC.get(), &APZCTreeManager::ContentReceivedInputBlock, aInputBlockId,
aEvent->mFlags.mDefaultPrevented));
defaultPrevented));
}
return status;