This commit is contained in:
Wes Kocher 2014-10-14 17:35:16 -07:00
Родитель 5e0d108df8 72c1b1df51
Коммит 08f61089ac
73 изменённых файлов: 1091 добавлений и 690 удалений

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

@ -21,6 +21,12 @@ XPCOMUtils.defineLazyGetter(this, "discovery", function() {
return devtools.require("devtools/toolkit/discovery/discovery");
});
XPCOMUtils.defineLazyGetter(this, "B2GTabList", function() {
const { B2GTabList } =
devtools.require("resource://gre/modules/DebuggerActors.js");
return B2GTabList;
});
let RemoteDebugger = {
_promptDone: false,
_promptAnswer: false,
@ -91,15 +97,7 @@ let RemoteDebugger = {
{
let { Promise: promise } = Cu.import("resource://gre/modules/Promise.jsm", {});
let parameters = {
// We do not expose browser tab actors yet,
// but we still have to define tabList.getList(),
// otherwise, client won't be able to fetch global actors
// from listTabs request!
tabList: {
getList: function() {
return promise.resolve([]);
}
},
tabList: new B2GTabList(connection),
// Use an explicit global actor list to prevent exposing
// unexpected actors
globalActorFactories: restrictPrivileges ? {

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

@ -29,7 +29,7 @@ XPCOMUtils.defineLazyGetter(this, 'MemoryFront', function() {
return devtools.require('devtools/server/actors/memory').MemoryFront;
});
Cu.import('resource://gre/modules/AppFrames.jsm');
Cu.import('resource://gre/modules/Frames.jsm');
/**
* The Developer HUD is an on-device developer tool that displays widgets,
@ -80,9 +80,10 @@ let developerHUD = {
}
}
AppFrames.addObserver(this);
Frames.addObserver(this);
for (let frame of AppFrames.list()) {
let appFrames = Frames.list().filter(frame => frame.getAttribute('mozapp'));
for (let frame of appFrames) {
this.trackFrame(frame);
}
@ -100,7 +101,7 @@ let developerHUD = {
this.untrackFrame(frame);
}
AppFrames.removeObserver(this);
Frames.removeObserver(this);
this._client.close();
delete this._client;
@ -137,11 +138,19 @@ let developerHUD = {
}
},
onAppFrameCreated: function (frame, isFirstAppFrame) {
onFrameCreated: function (frame, isFirstAppFrame) {
let mozapp = frame.getAttribute('mozapp');
if (!mozapp) {
return;
}
this.trackFrame(frame);
},
onAppFrameDestroyed: function (frame, isLastAppFrame) {
onFrameDestroyed: function (frame, isLastAppFrame) {
let mozapp = frame.getAttribute('mozapp');
if (!mozapp) {
return;
}
this.untrackFrame(frame);
},

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

@ -0,0 +1,83 @@
/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- /
/* vim: set shiftwidth=2 tabstop=2 autoindent cindent expandtab: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */
"use strict";
const { Cu } = require("chrome");
const DevToolsUtils = require("devtools/toolkit/DevToolsUtils.js");
const promise = require("promise");
const { XPCOMUtils } = require("resource://gre/modules/XPCOMUtils.jsm");
const { BrowserTabList } = require("devtools/server/actors/webbrowser");
XPCOMUtils.defineLazyGetter(this, "Frames", function() {
const { Frames } =
Cu.import("resource://gre/modules/Frames.jsm", {});
return Frames;
});
/**
* Unlike the original BrowserTabList which iterates over XUL windows, we
* override many portions to refer to Frames for the info needed here.
*/
function B2GTabList(connection) {
BrowserTabList.call(this, connection);
this._listening = false;
}
B2GTabList.prototype = Object.create(BrowserTabList.prototype);
B2GTabList.prototype._getBrowsers = function() {
return Frames.list().filter(frame => {
// Ignore app frames
return !frame.getAttribute("mozapp");
});
};
B2GTabList.prototype._getSelectedBrowser = function() {
return this._getBrowsers().find(frame => {
// Find the one visible browser (if any)
return !frame.classList.contains("hidden");
});
};
B2GTabList.prototype._checkListening = function() {
// The conditions from BrowserTabList are merged here, since we must listen to
// all events with our observer design.
this._listenForEventsIf(this._onListChanged && this._mustNotify ||
this._actorByBrowser.size > 0);
};
B2GTabList.prototype._listenForEventsIf = function(shouldListen) {
if (this._listening != shouldListen) {
let op = shouldListen ? "addObserver" : "removeObserver";
Frames[op](this);
this._listening = shouldListen;
}
};
B2GTabList.prototype.onFrameCreated = function(frame) {
let mozapp = frame.getAttribute("mozapp");
if (mozapp) {
// Ignore app frames
return;
}
this._notifyListChanged();
this._checkListening();
};
B2GTabList.prototype.onFrameDestroyed = function(frame) {
let mozapp = frame.getAttribute("mozapp");
if (mozapp) {
// Ignore app frames
return;
}
let actor = this._actorByBrowser.get(frame);
if (actor) {
this._handleActorClose(actor, frame);
}
};
exports.B2GTabList = B2GTabList;

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

@ -4,7 +4,7 @@
'use strict';
this.EXPORTED_SYMBOLS = ['AppFrames'];
this.EXPORTED_SYMBOLS = ['Frames'];
const Cu = Components.utils;
const Ci = Components.interfaces;
@ -27,11 +27,13 @@ const Observer = {
Services.obs.addObserver(this, 'inprocess-browser-shown', false);
Services.obs.addObserver(this, 'message-manager-disconnect', false);
SystemAppProxy.getAppFrames().forEach((frame) => {
SystemAppProxy.getFrames().forEach(frame => {
let mm = frame.QueryInterface(Ci.nsIFrameLoaderOwner).frameLoader.messageManager;
this._frames.set(mm, frame);
let mozapp = frame.getAttribute('mozapp');
this._apps.set(mozapp, (this._apps.get(mozapp) || 0) + 1);
if (mozapp) {
this._apps.set(mozapp, (this._apps.get(mozapp) || 0) + 1);
}
});
},
@ -68,16 +70,20 @@ const Observer = {
onMessageManagerCreated: function (mm, frame) {
this._frames.set(mm, frame);
let isFirstAppFrame = null;
let mozapp = frame.getAttribute('mozapp');
let count = (this._apps.get(mozapp) || 0) + 1;
this._apps.set(mozapp, count);
if (mozapp) {
let count = (this._apps.get(mozapp) || 0) + 1;
this._apps.set(mozapp, count);
isFirstAppFrame = (count === 1);
}
let isFirstAppFrame = (count === 1);
listeners.forEach(function (listener) {
try {
listener.onAppFrameCreated(frame, isFirstAppFrame);
listener.onFrameCreated(frame, isFirstAppFrame);
} catch(e) {
dump('Exception while calling Frames.jsm listener:' + e + '\n' + e.stack + '\n');
dump('Exception while calling Frames.jsm listener:' + e + '\n' +
e.stack + '\n');
}
});
},
@ -85,31 +91,35 @@ const Observer = {
onMessageManagerDestroyed: function (mm) {
let frame = this._frames.get(mm);
if (!frame) {
// We receive an event for a non mozapp message manager
// We received an event for an unknown message manager
return;
}
this._frames.delete(mm);
let isLastAppFrame = null;
let mozapp = frame.getAttribute('mozapp');
let count = (this._apps.get(mozapp) || 0) - 1;
this._apps.set(mozapp, count);
if (mozapp) {
let count = (this._apps.get(mozapp) || 0) - 1;
this._apps.set(mozapp, count);
isLastAppFrame = (count === 0);
}
let isLastAppFrame = (count === 0);
listeners.forEach(function (listener) {
try {
listener.onAppFrameDestroyed(frame, isLastAppFrame);
listener.onFrameDestroyed(frame, isLastAppFrame);
} catch(e) {
dump('Exception while calling Frames.jsm listener:' + e + '\n' + e.stack + '\n');
dump('Exception while calling Frames.jsm listener:' + e + '\n' +
e.stack + '\n');
}
});
}
};
let AppFrames = this.AppFrames = {
let Frames = this.Frames = {
list: () => SystemAppProxy.getAppFrames(),
list: () => SystemAppProxy.getFrames(),
addObserver: function (listener) {
if (listeners.indexOf(listener) !== -1) {

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

@ -117,23 +117,16 @@ let SystemAppProxy = {
}
},
getAppFrames: function systemApp_getAppFrames() {
getFrames: function systemApp_getFrames() {
let systemAppFrame = this._frame;
if (!systemAppFrame) {
return [];
}
let list = [systemAppFrame];
// List all app frames hosted in the system app: the homescreen,
// all regular apps, activities, rocket bar, attention screen and the keyboard.
// Bookmark apps and other system app internal frames like captive portal
// are also hosted in system app, but they are not using mozapp attribute.
let frames = systemAppFrame.contentDocument.querySelectorAll("iframe[mozapp]");
let frames = systemAppFrame.contentDocument.querySelectorAll('iframe');
for (let i = 0; i < frames.length; i++) {
list.push(frames[i]);
}
return list;
}
};

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

@ -48,9 +48,10 @@ if CONFIG['MOZ_UPDATER']:
EXTRA_JS_MODULES += [
'AlertsHelper.jsm',
'AppFrames.jsm',
'ContentRequestHelper.jsm',
'DebuggerActors.js',
'ErrorPage.jsm',
'Frames.jsm',
'FxAccountsMgmtService.jsm',
'LogCapture.jsm',
'LogParser.jsm',

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

@ -15,7 +15,7 @@
<project name="platform_build" path="build" remote="b2g" revision="3a2947df41a480de1457a6dcdbf46ad0af70d8e0">
<copyfile dest="Makefile" src="core/root.mk"/>
</project>
<project name="gaia" path="gaia" remote="mozillaorg" revision="de254419f3553f48187d003ee8e38034b429f069"/>
<project name="gaia" path="gaia" remote="mozillaorg" revision="5f1f0960ae9d22acf2a324ad37a48174d6df87f6"/>
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="cc1f362ce43dce92ac786187ff4abf39060094bd"/>
<project name="librecovery" path="librecovery" remote="b2g" revision="891e5069c0ad330d8191bf8c7b879c814258c89f"/>
@ -23,7 +23,7 @@
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
<project name="valgrind" path="external/valgrind" remote="b2g" revision="daa61633c32b9606f58799a3186395fd2bbb8d8c"/>
<project name="vex" path="external/VEX" remote="b2g" revision="47f031c320888fe9f3e656602588565b52d43010"/>
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="6ca2008ac50b163d31244ef9f036cb224f4f229b"/>
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="9f6b7471c881ee689183d681658cf2ba3dfc5610"/>
<!-- Stock Android things -->
<project groups="linux" name="platform/prebuilts/gcc/linux-x86/host/i686-linux-glibc2.7-4.6" path="prebuilts/gcc/linux-x86/host/i686-linux-glibc2.7-4.6" revision="95bb5b66b3ec5769c3de8d3f25d681787418e7d2"/>
<project groups="linux" name="platform/prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.7-4.6" path="prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.7-4.6" revision="ebdad82e61c16772f6cd47e9f11936bf6ebe9aa0"/>

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

@ -19,13 +19,13 @@
<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="de254419f3553f48187d003ee8e38034b429f069"/>
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="5f1f0960ae9d22acf2a324ad37a48174d6df87f6"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="cc1f362ce43dce92ac786187ff4abf39060094bd"/>
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
<project name="platform_hardware_ril" path="hardware/ril" remote="b2g" revision="cd88d860656c31c7da7bb310d6a160d0011b0961"/>
<project name="platform_external_qemu" path="external/qemu" remote="b2g" revision="c058843242068d0df7c107e09da31b53d2e08fa6"/>
<project name="moztt" path="external/moztt" remote="b2g" revision="562d357b72279a9e35d4af5aeecc8e1ffa2f44f1"/>
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="6ca2008ac50b163d31244ef9f036cb224f4f229b"/>
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="9f6b7471c881ee689183d681658cf2ba3dfc5610"/>
<!-- Stock Android things -->
<project name="platform/abi/cpp" path="abi/cpp" revision="dd924f92906085b831bf1cbbc7484d3c043d613c"/>
<project name="platform/bionic" path="bionic" revision="c72b8f6359de7ed17c11ddc9dfdde3f615d188a9"/>

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

@ -17,10 +17,10 @@
</project>
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
<project name="gaia" path="gaia" remote="mozillaorg" revision="de254419f3553f48187d003ee8e38034b429f069"/>
<project name="gaia" path="gaia" remote="mozillaorg" revision="5f1f0960ae9d22acf2a324ad37a48174d6df87f6"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="cc1f362ce43dce92ac786187ff4abf39060094bd"/>
<project name="moztt" path="external/moztt" remote="b2g" revision="562d357b72279a9e35d4af5aeecc8e1ffa2f44f1"/>
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="6ca2008ac50b163d31244ef9f036cb224f4f229b"/>
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="9f6b7471c881ee689183d681658cf2ba3dfc5610"/>
<project name="valgrind" path="external/valgrind" remote="b2g" revision="daa61633c32b9606f58799a3186395fd2bbb8d8c"/>
<project name="vex" path="external/VEX" remote="b2g" revision="47f031c320888fe9f3e656602588565b52d43010"/>
<!-- Stock Android things -->

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

@ -15,7 +15,7 @@
<project name="platform_build" path="build" remote="b2g" revision="3a2947df41a480de1457a6dcdbf46ad0af70d8e0">
<copyfile dest="Makefile" src="core/root.mk"/>
</project>
<project name="gaia" path="gaia" remote="mozillaorg" revision="de254419f3553f48187d003ee8e38034b429f069"/>
<project name="gaia" path="gaia" remote="mozillaorg" revision="5f1f0960ae9d22acf2a324ad37a48174d6df87f6"/>
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="cc1f362ce43dce92ac786187ff4abf39060094bd"/>
<project name="librecovery" path="librecovery" remote="b2g" revision="891e5069c0ad330d8191bf8c7b879c814258c89f"/>
@ -23,7 +23,7 @@
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
<project name="valgrind" path="external/valgrind" remote="b2g" revision="daa61633c32b9606f58799a3186395fd2bbb8d8c"/>
<project name="vex" path="external/VEX" remote="b2g" revision="47f031c320888fe9f3e656602588565b52d43010"/>
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="6ca2008ac50b163d31244ef9f036cb224f4f229b"/>
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="9f6b7471c881ee689183d681658cf2ba3dfc5610"/>
<!-- Stock Android things -->
<project groups="linux" name="platform/prebuilts/gcc/linux-x86/host/i686-linux-glibc2.7-4.6" path="prebuilts/gcc/linux-x86/host/i686-linux-glibc2.7-4.6" revision="f92a936f2aa97526d4593386754bdbf02db07a12"/>
<project groups="linux" name="platform/prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.7-4.6" path="prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.7-4.6" revision="6e47ff2790f5656b5b074407829ceecf3e6188c4"/>

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

@ -19,13 +19,13 @@
<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="de254419f3553f48187d003ee8e38034b429f069"/>
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="5f1f0960ae9d22acf2a324ad37a48174d6df87f6"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="cc1f362ce43dce92ac786187ff4abf39060094bd"/>
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
<project name="platform_hardware_ril" path="hardware/ril" remote="b2g" revision="cd88d860656c31c7da7bb310d6a160d0011b0961"/>
<project name="platform_external_qemu" path="external/qemu" remote="b2g" revision="c058843242068d0df7c107e09da31b53d2e08fa6"/>
<project name="moztt" path="external/moztt" remote="b2g" revision="562d357b72279a9e35d4af5aeecc8e1ffa2f44f1"/>
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="6ca2008ac50b163d31244ef9f036cb224f4f229b"/>
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="9f6b7471c881ee689183d681658cf2ba3dfc5610"/>
<!-- Stock Android things -->
<project name="platform/abi/cpp" path="abi/cpp" revision="dd924f92906085b831bf1cbbc7484d3c043d613c"/>
<project name="platform/bionic" path="bionic" revision="c72b8f6359de7ed17c11ddc9dfdde3f615d188a9"/>

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

@ -15,7 +15,7 @@
<project name="platform_build" path="build" remote="b2g" revision="3a2947df41a480de1457a6dcdbf46ad0af70d8e0">
<copyfile dest="Makefile" src="core/root.mk"/>
</project>
<project name="gaia" path="gaia" remote="mozillaorg" revision="de254419f3553f48187d003ee8e38034b429f069"/>
<project name="gaia" path="gaia" remote="mozillaorg" revision="5f1f0960ae9d22acf2a324ad37a48174d6df87f6"/>
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="cc1f362ce43dce92ac786187ff4abf39060094bd"/>
<project name="librecovery" path="librecovery" remote="b2g" revision="891e5069c0ad330d8191bf8c7b879c814258c89f"/>
@ -23,7 +23,7 @@
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
<project name="valgrind" path="external/valgrind" remote="b2g" revision="daa61633c32b9606f58799a3186395fd2bbb8d8c"/>
<project name="vex" path="external/VEX" remote="b2g" revision="47f031c320888fe9f3e656602588565b52d43010"/>
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="6ca2008ac50b163d31244ef9f036cb224f4f229b"/>
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="9f6b7471c881ee689183d681658cf2ba3dfc5610"/>
<!-- Stock Android things -->
<project groups="linux" name="platform/prebuilts/gcc/linux-x86/host/i686-linux-glibc2.7-4.6" path="prebuilts/gcc/linux-x86/host/i686-linux-glibc2.7-4.6" revision="95bb5b66b3ec5769c3de8d3f25d681787418e7d2"/>
<project groups="linux" name="platform/prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.7-4.6" path="prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.7-4.6" revision="ebdad82e61c16772f6cd47e9f11936bf6ebe9aa0"/>
@ -133,7 +133,7 @@
<project name="device/generic/armv7-a-neon" path="device/generic/armv7-a-neon" revision="1bb28abbc215f45220620af5cd60a8ac1be93722"/>
<project name="device/qcom/common" path="device/qcom/common" revision="54c32c2ddef066fbdf611d29e4b7c47e0363599e"/>
<project name="device-flame" path="device/t2m/flame" remote="b2g" revision="52c909e821d107d414f851e267dedcd7aae2cebf"/>
<project name="codeaurora_kernel_msm" path="kernel" remote="b2g" revision="1072f7d31dc0bf3a2adc64177b1104da9f4ce4b6"/>
<project name="codeaurora_kernel_msm" path="kernel" remote="b2g" revision="7731d63c809dbca4da408e1de0c1a044f0765e52"/>
<project name="kernel_lk" path="bootable/bootloader/lk" remote="b2g" revision="fda40423ffa573dc6cafd3780515010cb2a086be"/>
<project name="platform/external/bluetooth/bluedroid" path="external/bluetooth/bluedroid" revision="30b96dfca99cb384bf520a16b81f3aba56f09907"/>
<project name="platform/external/wpa_supplicant_8" path="external/wpa_supplicant_8" revision="5b71e40213f650459e95d35b6f14af7e88d8ab62"/>

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

@ -17,10 +17,10 @@
</project>
<project name="librecovery" path="librecovery" remote="b2g" revision="891e5069c0ad330d8191bf8c7b879c814258c89f"/>
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
<project name="gaia" path="gaia" remote="mozillaorg" revision="de254419f3553f48187d003ee8e38034b429f069"/>
<project name="gaia" path="gaia" remote="mozillaorg" revision="5f1f0960ae9d22acf2a324ad37a48174d6df87f6"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="cc1f362ce43dce92ac786187ff4abf39060094bd"/>
<project name="moztt" path="external/moztt" remote="b2g" revision="562d357b72279a9e35d4af5aeecc8e1ffa2f44f1"/>
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="6ca2008ac50b163d31244ef9f036cb224f4f229b"/>
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="9f6b7471c881ee689183d681658cf2ba3dfc5610"/>
<project name="valgrind" path="external/valgrind" remote="b2g" revision="daa61633c32b9606f58799a3186395fd2bbb8d8c"/>
<project name="vex" path="external/VEX" remote="b2g" revision="47f031c320888fe9f3e656602588565b52d43010"/>
<!-- Stock Android things -->

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

@ -4,6 +4,6 @@
"remote": "",
"branch": ""
},
"revision": "3e3eb020773e765f4cb7775356ae9c22896db875",
"revision": "b4f77d39d6d8eb153ede94f8a0d70247ccad74e8",
"repo_path": "/integration/gaia-central"
}

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

@ -17,12 +17,12 @@
<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="de254419f3553f48187d003ee8e38034b429f069"/>
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="5f1f0960ae9d22acf2a324ad37a48174d6df87f6"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="cc1f362ce43dce92ac786187ff4abf39060094bd"/>
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
<project name="librecovery" path="librecovery" remote="b2g" revision="891e5069c0ad330d8191bf8c7b879c814258c89f"/>
<project name="moztt" path="external/moztt" remote="b2g" revision="562d357b72279a9e35d4af5aeecc8e1ffa2f44f1"/>
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="6ca2008ac50b163d31244ef9f036cb224f4f229b"/>
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="9f6b7471c881ee689183d681658cf2ba3dfc5610"/>
<!-- Stock Android things -->
<project name="platform/abi/cpp" path="abi/cpp" revision="6426040f1be4a844082c9769171ce7f5341a5528"/>
<project name="platform/bionic" path="bionic" revision="d2eb6c7b6e1bc7643c17df2d9d9bcb1704d0b9ab"/>

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

@ -15,7 +15,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="de254419f3553f48187d003ee8e38034b429f069"/>
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="5f1f0960ae9d22acf2a324ad37a48174d6df87f6"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="cc1f362ce43dce92ac786187ff4abf39060094bd"/>
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
<project name="librecovery" path="librecovery" remote="b2g" revision="891e5069c0ad330d8191bf8c7b879c814258c89f"/>

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

@ -17,10 +17,10 @@
</project>
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
<project name="gaia" path="gaia" remote="mozillaorg" revision="de254419f3553f48187d003ee8e38034b429f069"/>
<project name="gaia" path="gaia" remote="mozillaorg" revision="5f1f0960ae9d22acf2a324ad37a48174d6df87f6"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="cc1f362ce43dce92ac786187ff4abf39060094bd"/>
<project name="moztt" path="external/moztt" remote="b2g" revision="562d357b72279a9e35d4af5aeecc8e1ffa2f44f1"/>
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="6ca2008ac50b163d31244ef9f036cb224f4f229b"/>
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="9f6b7471c881ee689183d681658cf2ba3dfc5610"/>
<project name="valgrind" path="external/valgrind" remote="b2g" revision="daa61633c32b9606f58799a3186395fd2bbb8d8c"/>
<project name="vex" path="external/VEX" remote="b2g" revision="47f031c320888fe9f3e656602588565b52d43010"/>
<!-- Stock Android things -->

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

@ -17,12 +17,12 @@
<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="de254419f3553f48187d003ee8e38034b429f069"/>
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="5f1f0960ae9d22acf2a324ad37a48174d6df87f6"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="cc1f362ce43dce92ac786187ff4abf39060094bd"/>
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
<project name="librecovery" path="librecovery" remote="b2g" revision="891e5069c0ad330d8191bf8c7b879c814258c89f"/>
<project name="moztt" path="external/moztt" remote="b2g" revision="562d357b72279a9e35d4af5aeecc8e1ffa2f44f1"/>
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="6ca2008ac50b163d31244ef9f036cb224f4f229b"/>
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="9f6b7471c881ee689183d681658cf2ba3dfc5610"/>
<project name="gonk-patches" path="patches" remote="b2g" revision="223a2421006e8f5da33f516f6891c87cae86b0f6"/>
<!-- Stock Android things -->
<project name="platform/abi/cpp" path="abi/cpp" revision="6426040f1be4a844082c9769171ce7f5341a5528"/>

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

@ -1292,6 +1292,7 @@ pref("services.sync.prefs.sync.xpinstall.whitelist.required", true);
// Developer edition preferences
pref("browser.devedition.theme.enabled", false);
pref("browser.devedition.theme.showCustomizeButton", false);
// Disable the error console
pref("devtools.errorconsole.enabled", false);

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

@ -37,6 +37,7 @@ const kPrefCustomizationState = "browser.uiCustomization.state";
const kPrefCustomizationAutoAdd = "browser.uiCustomization.autoAdd";
const kPrefCustomizationDebug = "browser.uiCustomization.debug";
const kPrefDrawInTitlebar = "browser.tabs.drawInTitlebar";
const kPrefDeveditionTheme = "browser.devedition.theme.enabled";
/**
* The keys are the handlers that are fired when the event type (the value)
@ -139,6 +140,7 @@ let gListeners = new Set();
let gUIStateBeforeReset = {
uiCustomizationState: null,
drawInTitlebar: null,
gUIStateBeforeReset: null,
};
let gModuleName = "[CustomizableUI]";
@ -2299,6 +2301,7 @@ let CustomizableUIInternal = {
_resetUIState: function() {
try {
gUIStateBeforeReset.drawInTitlebar = Services.prefs.getBoolPref(kPrefDrawInTitlebar);
gUIStateBeforeReset.deveditionTheme = Services.prefs.getBoolPref(kPrefDeveditionTheme);
gUIStateBeforeReset.uiCustomizationState = Services.prefs.getCharPref(kPrefCustomizationState);
} catch(e) { }
@ -2306,6 +2309,7 @@ let CustomizableUIInternal = {
Services.prefs.clearUserPref(kPrefCustomizationState);
Services.prefs.clearUserPref(kPrefDrawInTitlebar);
Services.prefs.clearUserPref(kPrefDeveditionTheme);
LOG("State reset");
// Reset placements to make restoring default placements possible.
@ -2367,13 +2371,15 @@ let CustomizableUIInternal = {
*/
undoReset: function() {
if (gUIStateBeforeReset.uiCustomizationState == null ||
gUIStateBeforeReset.drawInTitlebar == null) {
gUIStateBeforeReset.drawInTitlebar == null ||
gUIStateBeforeReset.deveditionTheme == null) {
return;
}
gUndoResetting = true;
let uiCustomizationState = gUIStateBeforeReset.uiCustomizationState;
let drawInTitlebar = gUIStateBeforeReset.drawInTitlebar;
let deveditionTheme = gUIStateBeforeReset.deveditionTheme;
// Need to clear the previous state before setting the prefs
// because pref observers may check if there is a previous UI state.
@ -2381,6 +2387,7 @@ let CustomizableUIInternal = {
Services.prefs.setCharPref(kPrefCustomizationState, uiCustomizationState);
Services.prefs.setBoolPref(kPrefDrawInTitlebar, drawInTitlebar);
Services.prefs.setBoolPref(kPrefDeveditionTheme, deveditionTheme);
this.loadSavedState();
// If the user just customizes toolbar/titlebar visibility, gSavedState will be null
// and we don't need to do anything else here:
@ -2558,6 +2565,10 @@ let CustomizableUIInternal = {
LOG(kPrefDrawInTitlebar + " pref is non-default");
return false;
}
if (Services.prefs.prefHasUserValue(kPrefDeveditionTheme)) {
LOG(kPrefDeveditionTheme + " pref is non-default");
return false;
}
return true;
},
@ -3258,7 +3269,8 @@ this.CustomizableUI = {
*/
get canUndoReset() {
return gUIStateBeforeReset.uiCustomizationState != null ||
gUIStateBeforeReset.drawInTitlebar != null;
gUIStateBeforeReset.drawInTitlebar != null ||
gUIStateBeforeReset.deveditionTheme != null;
},
/**

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

@ -17,6 +17,8 @@ const kPlaceholderClass = "panel-customization-placeholder";
const kSkipSourceNodePref = "browser.uiCustomization.skipSourceNodeCheck";
const kToolbarVisibilityBtn = "customization-toolbar-visibility-button";
const kDrawInTitlebarPref = "browser.tabs.drawInTitlebar";
const kDeveditionThemePref = "browser.devedition.theme.enabled";
const kDeveditionButtonPref = "browser.devedition.theme.showCustomizeButton";
const kMaxTransitionDurationMs = 2000;
const kPanelItemContextMenu = "customizationPanelItemContextMenu";
@ -69,8 +71,11 @@ function CustomizeMode(aWindow) {
#ifdef CAN_DRAW_IN_TITLEBAR
this._updateTitlebarButton();
Services.prefs.addObserver(kDrawInTitlebarPref, this, false);
this.window.addEventListener("unload", this);
#endif
this._updateDevEditionThemeButton();
Services.prefs.addObserver(kDeveditionButtonPref, this, false);
Services.prefs.addObserver(kDeveditionThemePref, this, false);
this.window.addEventListener("unload", this);
};
CustomizeMode.prototype = {
@ -105,6 +110,8 @@ CustomizeMode.prototype = {
#ifdef CAN_DRAW_IN_TITLEBAR
Services.prefs.removeObserver(kDrawInTitlebarPref, this);
#endif
Services.prefs.removeObserver(kDeveditionButtonPref, this);
Services.prefs.removeObserver(kDeveditionThemePref, this);
},
toggle: function() {
@ -1447,11 +1454,9 @@ CustomizeMode.prototype = {
this.exit();
}
break;
#ifdef CAN_DRAW_IN_TITLEBAR
case "unload":
this.uninit();
break;
#endif
}
},
@ -1463,6 +1468,7 @@ CustomizeMode.prototype = {
#ifdef CAN_DRAW_IN_TITLEBAR
this._updateTitlebarButton();
#endif
this._updateDevEditionThemeButton();
break;
case "lightweight-theme-window-updated":
if (aSubject == this.window) {
@ -1498,6 +1504,29 @@ CustomizeMode.prototype = {
},
#endif
_updateDevEditionThemeButton: function() {
let button = this.document.getElementById("customization-devedition-theme-button");
let themeEnabled = Services.prefs.getBoolPref(kDeveditionThemePref);
if (themeEnabled) {
button.setAttribute("checked", "true");
} else {
button.removeAttribute("checked");
}
let buttonVisible = Services.prefs.getBoolPref(kDeveditionButtonPref);
if (buttonVisible) {
button.removeAttribute("hidden");
} else {
button.setAttribute("hidden", "true");
}
},
toggleDevEditionTheme: function() {
let button = this.document.getElementById("customization-devedition-theme-button");
let preferenceValue = button.hasAttribute("checked");
Services.prefs.setBoolPref(kDeveditionThemePref, preferenceValue);
},
_onDragStart: function(aEvent) {
__dumpDragData(aEvent);
let item = aEvent.target;

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

@ -52,6 +52,14 @@
</hbox>
</panel>
</button>
<button id="customization-devedition-theme-button"
class="customizationmode-button"
hidden="true"
label="&customizeMode.deveditionTheme.label;"
oncommand="gCustomizeMode.toggleDevEditionTheme()"
type="checkbox" />
<spacer id="customization-footer-spacer"/>
<button id="customization-undo-reset-button"
class="customizationmode-button"

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

@ -101,6 +101,44 @@ add_task(function() {
is(undoResetButton.hidden, true, "Undo reset button should be hidden at end of test");
});
// Bug 1082108 - Restore Defaults should clear user pref for devedition theme
add_task(function() {
let prefName = "browser.devedition.theme.enabled";
Services.prefs.setBoolPref("browser.devedition.theme.showCustomizeButton", true);
let defaultValue = Services.prefs.getBoolPref(prefName);
let restoreDefaultsButton = document.getElementById("customization-reset-button");
let deveditionThemeButton = document.getElementById("customization-devedition-theme-button");
let undoResetButton = document.getElementById("customization-undo-reset-button");
ok(CustomizableUI.inDefaultState, "Should be in default state at start of test");
ok(restoreDefaultsButton.disabled, "Restore defaults button should be disabled when in default state");
is(deveditionThemeButton.hasAttribute("checked"), defaultValue, "Devedition theme button should reflect pref value");
is(undoResetButton.hidden, true, "Undo reset button should be hidden at start of test");
Services.prefs.setBoolPref(prefName, !defaultValue);
ok(!restoreDefaultsButton.disabled, "Restore defaults button should be enabled when pref changed");
is(deveditionThemeButton.hasAttribute("checked"), !defaultValue, "Devedition theme button should reflect changed pref value");
ok(!CustomizableUI.inDefaultState, "With devedition theme flipped, no longer default");
is(undoResetButton.hidden, true, "Undo reset button should be hidden after pref change");
yield gCustomizeMode.reset();
ok(restoreDefaultsButton.disabled, "Restore defaults button should be disabled after reset");
is(deveditionThemeButton.hasAttribute("checked"), defaultValue, "devedition theme button should reflect default value after reset");
is(Services.prefs.getBoolPref(prefName), defaultValue, "Reset should reset devedition.theme.enabled");
ok(CustomizableUI.inDefaultState, "In default state after devedition theme reset");
is(undoResetButton.hidden, false, "Undo reset button should be visible after reset");
ok(!undoResetButton.disabled, "Undo reset button should be enabled after reset");
yield gCustomizeMode.undoReset();
ok(!restoreDefaultsButton.disabled, "Restore defaults button should be enabled after undo-reset");
is(deveditionThemeButton.hasAttribute("checked"), !defaultValue, "devedition theme button should reflect undo-reset value");
ok(!CustomizableUI.inDefaultState, "No longer in default state after undo");
is(Services.prefs.getBoolPref(prefName), !defaultValue, "Undo-reset goes back to previous pref value");
is(undoResetButton.hidden, true, "Undo reset button should be hidden after undo-reset clicked");
Services.prefs.clearUserPref(prefName);
ok(CustomizableUI.inDefaultState, "In default state after pref cleared");
is(undoResetButton.hidden, true, "Undo reset button should be hidden at end of test");
});
add_task(function asyncCleanup() {
yield gCustomizeMode.reset();
yield endCustomizing();

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

@ -40,7 +40,17 @@ this.EXPORTED_SYMBOLS = ["injectLoopAPI"];
const cloneErrorObject = function(error, targetWindow) {
let obj = new targetWindow.Error();
for (let prop of Object.getOwnPropertyNames(error)) {
obj[prop] = String(error[prop]);
let value = error[prop];
if (typeof value != "string" && typeof value != "number") {
value = String(value);
}
Object.defineProperty(Cu.waiveXrays(obj), prop, {
configurable: false,
enumerable: true,
value: value,
writable: false
});
}
return obj;
};

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

@ -44,6 +44,43 @@ XPCOMUtils.defineLazyModuleGetter(this, "Weave",
// copied from utilityOverlay.js
const TAB_DROP_TYPE = "application/x-moz-tabbrowser-tab";
// This function isn't public both because it's synchronous and because it is
// going to be removed in bug 1072833.
function IsLivemark(aItemId) {
// Since this check may be done on each dragover event, it's worth maintaining
// a cache.
let self = IsLivemark;
if (!("ids" in self)) {
const LIVEMARK_ANNO = PlacesUtils.LMANNO_FEEDURI;
let idsVec = PlacesUtils.annotations.getItemsWithAnnotation(LIVEMARK_ANNO);
self.ids = new Set(idsVec);
let obs = Object.freeze({
QueryInterface: XPCOMUtils.generateQI(Ci.nsIAnnotationObserver),
onItemAnnotationSet(itemId, annoName) {
if (annoName == LIVEMARK_ANNO)
self.ids.add(itemId);
},
onItemAnnotationRemoved(itemId, annoName) {
// If annoName is set to an empty string, the item is gone.
if (annoName == LIVEMARK_ANNO || annoName == "")
self.ids.delete(itemId);
},
onPageAnnotationSet() { },
onPageAnnotationRemoved() { },
});
PlacesUtils.annotations.addObserver(obs);
PlacesUtils.registerShutdownFunction(() => {
PlacesUtils.annotations.removeObserver(obs);
});
}
return self.ids.has(aItemId);
}
this.PlacesUIUtils = {
ORGANIZER_LEFTPANE_VERSION: 7,
ORGANIZER_FOLDER_ANNO: "PlacesOrganizer/OrganizerFolder",
@ -560,6 +597,89 @@ this.PlacesUIUtils = {
return "";
},
/**
* Check whether or not the given node represents a removable entry (either in
* history or in bookmarks).
*
* @param aNode
* a node, except the root node of a query.
* @return true if the aNode represents a removable entry, false otherwise.
*/
canUserRemove: function (aNode) {
let parentNode = aNode.parent;
if (!parentNode)
throw new Error("canUserRemove doesn't accept root nodes");
// If it's not a bookmark, we can remove it unless it's a child of a
// livemark.
if (aNode.itemId == -1) {
// Rather than executing a db query, checking the existence of the feedURI
// annotation, detect livemark children by the fact that they are the only
// direct non-bookmark children of bookmark folders.
return !PlacesUtils.nodeIsFolder(parentNode);
}
// Otherwise it has to be a child of an editable folder.
return !this.isContentsReadOnly(parentNode);
},
/**
* DO NOT USE THIS API IN ADDONS. IT IS VERY LIKELY TO CHANGE WHEN THE SWITCH
* TO GUIDS IS COMPLETE (BUG 1071511).
*
* Check whether or not the given node or item-id points to a folder which
* should not be modified by the user (i.e. its children should be unremovable
* and unmovable, new children should be disallowed, etc).
* These semantics are not inherited, meaning that read-only folder may
* contain editable items (for instance, the places root is read-only, but all
* of its direct children aren't).
*
* You should only pass folder item ids or folder nodes for aNodeOrItemId.
* While this is only enforced for the node case (if an item id of a separator
* or a bookmark is passed, false is returned), it's considered the caller's
* job to ensure that it checks a folder.
* Also note that folder-shortcuts should only be passed as result nodes.
* Otherwise they are just treated as bookmarks (i.e. false is returned).
*
* @param aNodeOrItemId
* any item id or result node.
* @throws if aNodeOrItemId is neither an item id nor a folder result node.
* @note livemark "folders" are considered read-only (but see bug 1072833).
* @return true if aItemId points to a read-only folder, false otherwise.
*/
isContentsReadOnly: function (aNodeOrItemId) {
let itemId;
if (typeof(aNodeOrItemId) == "number") {
itemId = aNodeOrItemId;
}
else if (PlacesUtils.nodeIsFolder(aNodeOrItemId)) {
itemId = PlacesUtils.getConcreteItemId(aNodeOrItemId);
}
else {
throw new Error("invalid value for aNodeOrItemId");
}
if (itemId == PlacesUtils.placesRootId || IsLivemark(itemId))
return true;
// leftPaneFolderId, and as a result, allBookmarksFolderId, is a lazy getter
// performing at least a synchronous DB query (and on its very first call
// in a fresh profile, it also creates the entire structure).
// Therefore we don't want to this function, which is called very often by
// isCommandEnabled, to ever be the one that invokes it first, especially
// because isCommandEnabled may be called way before the left pane folder is
// even created (for example, if the user only uses the bookmarks menu or
// toolbar for managing bookmarks). To do so, we avoid comparing to those
// special folder if the lazy getter is still in place. This is safe merely
// because the only way to access the left pane contents goes through
// "resolving" the leftPaneFolderId getter.
if ("get" in Object.getOwnPropertyDescriptor(this, "leftPaneFolderId"))
return false;
return itemId == this.leftPaneFolderId ||
itemId == this.allBookmarksFolderId;
},
/**
* Gives the user a chance to cancel loading lots of tabs at once
*/
@ -962,8 +1082,6 @@ this.PlacesUIUtils = {
// We should never backup this, since it changes between profiles.
as.setItemAnnotation(folderId, PlacesUtils.EXCLUDE_FROM_BACKUP_ANNO, 1,
0, as.EXPIRE_NEVER);
// Disallow manipulating this folder within the organizer UI.
bs.setFolderReadonly(folderId, true);
if (aIsRoot) {
// Mark as special left pane root.

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

@ -1376,8 +1376,8 @@ PlacesToolbar.prototype = {
elt.localName != "menupopup") {
let eltRect = elt.getBoundingClientRect();
let eltIndex = Array.indexOf(this._rootElt.childNodes, elt);
if (PlacesUtils.nodeIsFolder(elt._placesNode) &&
!PlacesUtils.nodeIsReadOnly(elt._placesNode)) {
if (PlacesUIUtils.nodeIsFolder(elt._placesNode) &&
!PlacesUIUtils.isContentsReadOnly(elt._placesNode)) {
// This is a folder.
// If we are in the middle of it, drop inside it.
// Otherwise, drop before it, with regards to RTL mode.

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

@ -200,7 +200,7 @@ PlacesController.prototype = {
var selectedNode = this._view.selectedNode;
return selectedNode &&
PlacesUtils.nodeIsFolder(selectedNode) &&
!PlacesUtils.nodeIsReadOnly(selectedNode) &&
!PlacesUIUtils.isContentsReadOnly(selectedNode) &&
this._view.result.sortingMode ==
Ci.nsINavHistoryQueryOptions.SORT_BY_NONE;
case "placesCmd_createBookmark":
@ -330,21 +330,7 @@ PlacesController.prototype = {
if (nodes[i] == root)
return false;
if (PlacesUtils.nodeIsFolder(nodes[i]) &&
!PlacesControllerDragHelper.canMoveNode(nodes[i]))
return false;
// We don't call nodeIsReadOnly here, because nodeIsReadOnly means that
// a node has children that cannot be edited, reordered or removed. Here,
// we don't care if a node's children can't be reordered or edited, just
// that they're removable. All history results have removable children
// (based on the principle that any URL in the history table should be
// removable), but some special bookmark folders may have non-removable
// children, e.g. live bookmark folder children. It doesn't make sense
// to delete a child of a live bookmark folder, since when the folder
// refreshes, the child will return.
var parent = nodes[i].parent || root;
if (PlacesUtils.isReadonlyFolder(parent))
if (!PlacesUIUtils.canUserRemove(nodes[i]))
return false;
}
}
@ -1560,10 +1546,9 @@ let PlacesControllerDragHelper = {
canMoveUnwrappedNode: function (aUnwrappedNode) {
return aUnwrappedNode.id > 0 &&
!PlacesUtils.isRootItem(aUnwrappedNode.id) &&
aUnwrappedNode.parent != PlacesUtils.placesRootId &&
!PlacesUIUtils.isContentsReadOnly(aUnwrappedNode.parent) ||
aUnwrappedNode.parent != PlacesUtils.tagsFolderId &&
aUnwrappedNode.grandParentId != PlacesUtils.tagsFolderId &&
!aUnwrappedNode.parentReadOnly;
aUnwrappedNode.grandParentId != PlacesUtils.tagsFolderId;
},
/**
@ -1575,58 +1560,17 @@ let PlacesControllerDragHelper = {
*/
canMoveNode:
function PCDH_canMoveNode(aNode) {
// Can't move query root.
if (!aNode.parent)
// Only bookmark items are movable.
if (aNode.itemId == -1)
return false;
let parentId = PlacesUtils.getConcreteItemId(aNode.parent);
let concreteId = PlacesUtils.getConcreteItemId(aNode);
// Can't move children of tag containers.
if (PlacesUtils.nodeIsTagQuery(aNode.parent))
return false;
// Can't move children of read-only containers.
if (PlacesUtils.nodeIsReadOnly(aNode.parent))
return false;
// Check for special folders, etc.
if (PlacesUtils.nodeIsContainer(aNode) &&
!this.canMoveContainer(aNode.itemId, parentId))
return false;
return true;
},
/**
* Determines if a container node can be moved.
*
* @param aId
* A bookmark folder id.
* @param [optional] aParentId
* The parent id of the folder.
* @return True if the container can be moved to the target.
*/
canMoveContainer:
function PCDH_canMoveContainer(aId, aParentId) {
if (aId == -1)
return false;
// Disallow moving of roots and special folders.
const ROOTS = [PlacesUtils.placesRootId, PlacesUtils.bookmarksMenuFolderId,
PlacesUtils.tagsFolderId, PlacesUtils.unfiledBookmarksFolderId,
PlacesUtils.toolbarFolderId];
if (ROOTS.indexOf(aId) != -1)
return false;
// Get parent id if necessary.
if (aParentId == null || aParentId == -1)
aParentId = PlacesUtils.bookmarks.getFolderIdForItem(aId);
if (PlacesUtils.bookmarks.getFolderReadonly(aParentId))
return false;
return true;
// Once tags and bookmarked are divorced, the tag-query check should be
// removed.
let parentNode = aNode.parent;
return parentNode != null &&
!(PlacesUtils.nodeIsFolder(parentNode) &&
PlacesUIUtils.isContentsReadOnly(parentNode)) &&
!PlacesUtils.nodeIsTagQuery(parentNode);
},
/**
@ -1726,12 +1670,10 @@ let PlacesControllerDragHelper = {
*/
disallowInsertion: function(aContainer) {
NS_ASSERT(aContainer, "empty container");
// Allow dropping into Tag containers.
if (PlacesUtils.nodeIsTagQuery(aContainer))
return false;
// Disallow insertion of items under readonly folders.
return (!PlacesUtils.nodeIsFolder(aContainer) ||
PlacesUtils.nodeIsReadOnly(aContainer));
// Allow dropping into Tag containers and editable folders.
return !PlacesUtils.nodeIsTagQuery(aContainer) &&
(!PlacesUtils.nodeIsFolder(aContainer) ||
PlacesUIUtils.isContentsReadOnly(aContainer));
}
};

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

@ -106,9 +106,9 @@
let tagName = PlacesUtils.nodeIsTagQuery(elt._placesNode) ?
elt._placesNode.title : null;
if ((PlacesUtils.nodeIsFolder(elt._placesNode) ||
PlacesUtils.nodeIsTagQuery(elt._placesNode)) &&
!PlacesUtils.nodeIsReadOnly(elt._placesNode)) {
if ((PlacesUtils.nodeIsFolder(elt._placesNode) &&
!PlacesUIUtils.isContentsReadOnly(elt._placesNode)) ||
PlacesUtils.nodeIsTagQuery(elt._placesNode)) {
// This is a folder or a tag container.
if (eventY - eltY < eltHeight * 0.20) {
// If mouse is in the top part of the element, drop above folder.

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

@ -1649,23 +1649,39 @@ PlacesTreeView.prototype = {
if (aColumn.index != 0)
return false;
// Only bookmark-nodes are editable, and those are never built lazily
let node = this._rows[aRow];
if (!node || node.itemId == -1)
if (!node) {
Cu.reportError("isEditable called for an unbuilt row.");
return false;
}
let itemId = node.itemId;
// Only bookmark-nodes are editable. Fortunately, this checks also takes
// care of livemark children.
if (itemId == -1)
return false;
// The following items are never editable:
// * Read-only items.
// The following items are also not editable, even though they are bookmark
// items.
// * places-roots
// * the left pane special folders and queries (those are place: uri
// bookmarks)
// * separators
if (PlacesUtils.nodeIsReadOnly(node) ||
PlacesUtils.nodeIsSeparator(node))
//
// Note that concrete itemIds aren't used intentionally. For example, we
// have no reason to disallow renaming a shortcut to the Bookmarks Toolbar,
// except for the one under All Bookmarks.
if (PlacesUtils.nodeIsSeparator(node) || PlacesUtils.isRootItem(itemId))
return false;
if (PlacesUtils.nodeIsFolder(node)) {
let itemId = PlacesUtils.getConcreteItemId(node);
if (PlacesUtils.isRootItem(itemId))
return false;
let parentId = PlacesUtils.getConcreteItemId(node.parent);
if (parentId == PlacesUIUtils.leftPaneFolderId ||
parentId == PlacesUIUtils.allBookmarksFolderId) {
// Note that the for the time being this is the check that actually
// blocks renaming places "roots", and not the isRootItem check above.
// That's because places root are only exposed through folder shortcuts
// descendants of the left pane folder.
return false;
}
return true;

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

@ -27,8 +27,6 @@ function test() {
validate: function() {
is(rootNode.childCount, 1,
"populate added data to the test root");
is(PlacesControllerDragHelper.canMoveContainer(this.id),
true, "can move regular folder id");
is(PlacesControllerDragHelper.canMoveNode(rootNode.getChild(0)),
true, "can move regular folder node");
}
@ -57,9 +55,6 @@ function test() {
var concreteId = PlacesUtils.getConcreteItemId(shortcutNode);
is(concreteId, folderNode.itemId, "shortcut node id and concrete id match");
is(PlacesControllerDragHelper.canMoveContainer(this.shortcutId),
true, "can move folder shortcut id");
is(PlacesControllerDragHelper.canMoveNode(shortcutNode),
true, "can move folder shortcut node");
}
@ -83,9 +78,6 @@ function test() {
var queryNode = rootNode.getChild(1);
is(queryNode.itemId, this.queryId, "query id and query node item id match");
is(PlacesControllerDragHelper.canMoveContainer(this.queryId),
true, "can move query id");
is(PlacesControllerDragHelper.canMoveNode(queryNode),
true, "can move query node");
}
@ -127,9 +119,6 @@ function test() {
for (var i = 0; i < this.folders.length; i++) {
var id = this.folders[i];
is(PlacesControllerDragHelper.canMoveContainer(id),
false, "shouldn't be able to move special folder id");
var node = getRootChildNode(id);
isnot(node, null, "Node found");
is(PlacesControllerDragHelper.canMoveNode(node),
@ -140,10 +129,6 @@ function test() {
is(shortcutNode.itemId, shortcutId, "shortcut id and shortcut node item id match");
dump("can move shortcut id?\n");
is(PlacesControllerDragHelper.canMoveContainer(shortcutId),
true, "should be able to move special folder shortcut id");
dump("can move shortcut node?\n");
is(PlacesControllerDragHelper.canMoveNode(shortcutNode),
true, "should be able to move special folder shortcut node");
@ -169,46 +154,13 @@ function test() {
is(tagsNode.childCount, 1, "has new tag");
var tagNode = tagsNode.getChild(0);
is(PlacesControllerDragHelper.canMoveNode(tagNode),
false, "should not be able to move tag container node");
tagsNode.containerOpen = false;
}
});
// test that any child of a read-only node cannot be moved
tests.push({
populate: function() {
this.id =
PlacesUtils.bookmarks.createFolder(rootId, "foo", IDX);
PlacesUtils.bookmarks.createFolder(this.id, "bar", IDX);
PlacesUtils.bookmarks.setFolderReadonly(this.id, true);
},
validate: function() {
is(rootNode.childCount, 1,
"populate added data to the test root");
var readOnlyFolder = rootNode.getChild(0);
// test that we can move the read-only folder
is(PlacesControllerDragHelper.canMoveContainer(this.id),
true, "can move read-only folder id");
is(PlacesControllerDragHelper.canMoveNode(readOnlyFolder),
true, "can move read-only folder node");
// test that we cannot move the child of a read-only folder
readOnlyFolder.QueryInterface(Ci.nsINavHistoryContainerResultNode);
readOnlyFolder.containerOpen = true;
var childFolder = readOnlyFolder.getChild(0);
is(PlacesControllerDragHelper.canMoveContainer(childFolder.itemId),
false, "cannot move a child of a read-only folder");
is(PlacesControllerDragHelper.canMoveNode(childFolder),
false, "cannot move a child node of a read-only folder node");
readOnlyFolder.containerOpen = false;
}
});
tests.forEach(function(aTest) {
PlacesUtils.bookmarks.removeFolderChildren(rootId);
aTest.populate();

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

@ -130,6 +130,15 @@
-moz-appearance: none;
}
#customization-titlebar-visibility-button[checked],
#customization-devedition-theme-button[checked] {
background-color: rgb(218, 218, 218);
border-color: rgb(168, 168, 168);
text-shadow: 0 1px rgb(236, 236, 236);
box-shadow: 0 1px rgba(255, 255, 255, 0.5),
inset 0 1px rgb(196, 196, 196);
}
.customizationmode-button[disabled="true"] {
opacity: .5;
}
@ -156,11 +165,6 @@
#customization-titlebar-visibility-button[checked] {
-moz-image-region: rect(0, 48px, 24px, 24px);
background-color: rgb(218, 218, 218);
border-color: rgb(168, 168, 168);
text-shadow: 0 1px rgb(236, 236, 236);
box-shadow: 0 1px rgba(255, 255, 255, 0.5),
inset 0 1px rgb(196, 196, 196);
}
#main-window[customize-entered] #customization-panel-container {

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

@ -2408,10 +2408,18 @@ HTMLInputElement::SetUserInput(const nsAString& aValue)
SetValueInternal(aValue, true, true);
}
return nsContentUtils::DispatchTrustedEvent(OwnerDoc(),
static_cast<nsIDOMHTMLInputElement*>(this),
NS_LITERAL_STRING("input"), true,
true);
nsContentUtils::DispatchTrustedEvent(OwnerDoc(),
static_cast<nsIDOMHTMLInputElement*>(this),
NS_LITERAL_STRING("input"), true,
true);
// If this element is not currently focused, it won't receive a change event for this
// update through the normal channels. So fire a change event immediately, instead.
if (!ShouldBlur(this)) {
FireChangeEventIfNeeded();
}
return NS_OK;
}
nsIEditor*

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

@ -42,7 +42,7 @@ function testUserInput() {
"Change event dispatched when setting the value of the input element (2).");
SpecialPowers.wrap(input).setUserInput("foo");
is(inputChange, 1,
is(inputChange, 2,
"Change event dispatched when input element doesn't have focus.");
textarea.focus();

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

@ -2858,6 +2858,11 @@ nsDocShell::PopProfileTimelineMarkers(JSContext* aCx,
nsTArray<mozilla::dom::ProfileTimelineMarker> profileTimelineMarkers;
// If we see an unpaired START, we keep it around for the next call
// to PopProfileTimelineMarkers. We store the kept START objects in
// this array.
decltype(mProfileTimelineMarkers) keptMarkers;
for (uint32_t i = 0; i < mProfileTimelineMarkers.Length(); ++i) {
ProfilerMarkerTracing* startPayload = static_cast<ProfilerMarkerTracing*>(
mProfileTimelineMarkers[i]->mPayload);
@ -2866,6 +2871,8 @@ nsDocShell::PopProfileTimelineMarkers(JSContext* aCx,
bool hasSeenPaintedLayer = false;
if (startPayload->GetMetaData() == TRACING_INTERVAL_START) {
bool hasSeenEnd = false;
// The assumption is that the devtools timeline flushes markers frequently
// enough for the amount of markers to always be small enough that the
// nested for loop isn't going to be a performance problem.
@ -2893,15 +2900,26 @@ nsDocShell::PopProfileTimelineMarkers(JSContext* aCx,
profileTimelineMarkers.AppendElement(marker);
}
// We want the start to be dropped either way.
hasSeenEnd = true;
break;
}
}
// If we did not see the corresponding END, keep the START.
if (!hasSeenEnd) {
keptMarkers.AppendElement(mProfileTimelineMarkers[i]);
mProfileTimelineMarkers.RemoveElementAt(i);
--i;
}
}
}
ToJSValue(aCx, profileTimelineMarkers, aProfileTimelineMarkers);
ClearProfileTimelineMarkers();
mProfileTimelineMarkers.SwapElements(keptMarkers);
return NS_OK;
#else
@ -2952,8 +2970,7 @@ nsDocShell::ClearProfileTimelineMarkers()
{
#ifdef MOZ_ENABLE_PROFILER_SPS
for (uint32_t i = 0; i < mProfileTimelineMarkers.Length(); ++i) {
delete mProfileTimelineMarkers[i]->mPayload;
mProfileTimelineMarkers[i]->mPayload = nullptr;
delete mProfileTimelineMarkers[i];
}
mProfileTimelineMarkers.Clear();
#endif

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

@ -21,6 +21,7 @@
#include "mozilla/WeakPtr.h"
#include "mozilla/TimeStamp.h"
#include "GeckoProfiler.h"
#include "ProfilerMarkers.h"
// Helper Classes
#include "nsCOMPtr.h"
@ -959,11 +960,17 @@ private:
, mPayload(aPayload)
, mTime(aTime)
{}
~InternalProfileTimelineMarker()
{
delete mPayload;
}
const char* mName;
ProfilerMarkerTracing* mPayload;
float mTime;
};
nsTArray<nsAutoPtr<InternalProfileTimelineMarker>> mProfileTimelineMarkers;
nsTArray<InternalProfileTimelineMarker*> mProfileTimelineMarkers;
// Get the elapsed time (in millis) since the profile timeline recording
// started

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

@ -36,13 +36,16 @@ const NFC_ENABLED = libcutils.property_get("ro.moz.nfc.enabled", "false") === "t
let DEBUG = NFC.DEBUG_CONTENT_HELPER;
let debug;
if (DEBUG) {
debug = function (s) {
dump("-*- NfcContentHelper: " + s + "\n");
};
} else {
debug = function (s) {};
}
function updateDebug() {
if (DEBUG) {
debug = function (s) {
dump("-*- NfcContentHelper: " + s + "\n");
};
} else {
debug = function (s) {};
}
};
updateDebug();
const NFCCONTENTHELPER_CID =
Components.ID("{4d72c120-da5f-11e1-9b23-0800200c9a66}");
@ -65,6 +68,8 @@ XPCOMUtils.defineLazyServiceGetter(this, "cpmm",
function NfcContentHelper() {
this.initDOMRequestHelper(/* aWindow */ null, NFC_IPC_MSG_NAMES);
Services.obs.addObserver(this, NFC.TOPIC_MOZSETTINGS_CHANGED, false);
Services.obs.addObserver(this, "xpcom-shutdown", false);
this._requestMap = [];
@ -86,6 +91,17 @@ NfcContentHelper.prototype = {
_requestMap: null,
eventTarget: null,
init: function init(aWindow) {
if (aWindow && aWindow.navigator.mozSettings) {
let lock = aWindow.navigator.mozSettings.createLock();
var nfcDebug = lock.get(NFC.SETTING_NFC_DEBUG);
nfcDebug.onsuccess = function _nfcDebug() {
DEBUG = nfcDebug.result[NFC.SETTING_NFC_DEBUG];
updateDebug();
};
}
},
encodeNDEFRecords: function encodeNDEFRecords(records) {
let encodedRecords = [];
for (let i = 0; i < records.length; i++) {
@ -328,8 +344,16 @@ NfcContentHelper.prototype = {
observe: function observe(subject, topic, data) {
if (topic == "xpcom-shutdown") {
this.destroyDOMRequestHelper();
Services.obs.removeObserver(this, NFC.TOPIC_MOZSETTINGS_CHANGED);
Services.obs.removeObserver(this, "xpcom-shutdown");
cpmm = null;
} else if (topic == NFC.TOPIC_MOZSETTINGS_CHANGED) {
if ("wrappedJSObject" in subject) {
subject = subject.wrappedJSObject;
}
if (subject) {
this.handle(subject.key, subject.value);
}
}
},
@ -395,6 +419,15 @@ NfcContentHelper.prototype = {
}
},
handle: function handle(name, result) {
switch (name) {
case NFC.SETTING_NFC_DEBUG:
DEBUG = result;
updateDebug();
break;
}
},
handleReadNDEFResponse: function handleReadNDEFResponse(result) {
let requester = this._requestMap[result.requestId];
if (!requester) {

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

@ -22,6 +22,10 @@ const {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components;
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
Cu.import("resource://gre/modules/Services.jsm");
XPCOMUtils.defineLazyServiceGetter(this, "gSettingsService",
"@mozilla.org/settingsService;1",
"nsISettingsService");
XPCOMUtils.defineLazyGetter(this, "NFC", function () {
let obj = {};
Cu.import("resource://gre/modules/nfc_consts.js", obj);
@ -35,13 +39,16 @@ const NFC_ENABLED = libcutils.property_get("ro.moz.nfc.enabled", "false") === "t
let DEBUG = NFC.DEBUG_NFC;
let debug;
if (DEBUG) {
debug = function (s) {
dump("-*- Nfc: " + s + "\n");
};
} else {
debug = function (s) {};
}
function updateDebug() {
if (DEBUG) {
debug = function (s) {
dump("-*- Nfc: " + s + "\n");
};
} else {
debug = function (s) {};
}
};
updateDebug();
const NFC_CONTRACTID = "@mozilla.org/nfc;1";
const NFC_CID =
@ -103,6 +110,10 @@ XPCOMUtils.defineLazyGetter(this, "gMessageManager", function () {
init: function init(nfc) {
this.nfc = nfc;
let lock = gSettingsService.createLock();
lock.get(NFC.SETTING_NFC_DEBUG, this.nfc);
Services.obs.addObserver(this, NFC.TOPIC_MOZSETTINGS_CHANGED, false);
Services.obs.addObserver(this, NFC.TOPIC_XPCOM_SHUTDOWN, false);
this._registerMessageListeners();
},
@ -111,6 +122,7 @@ XPCOMUtils.defineLazyGetter(this, "gMessageManager", function () {
this.nfc.shutdown();
this.nfc = null;
Services.obs.removeObserver(this, NFC.TOPIC_MOZSETTINGS_CHANGED);
Services.obs.removeObserver(this, NFC.TOPIC_XPCOM_SHUTDOWN);
this._unregisterMessageListeners();
},
@ -335,6 +347,14 @@ XPCOMUtils.defineLazyGetter(this, "gMessageManager", function () {
observe: function observe(subject, topic, data) {
switch (topic) {
case NFC.TOPIC_MOZSETTINGS_CHANGED:
if ("wrappedJSObject" in subject) {
subject = subject.wrappedJSObject;
}
if (subject) {
this.nfc.handle(subject.key, subject.value);
}
break;
case NFC.TOPIC_XPCOM_SHUTDOWN:
this._shutdown();
break;
@ -649,6 +669,18 @@ Nfc.prototype = {
});
},
/**
* nsISettingsServiceCallback
*/
handle: function handle(name, result) {
switch (name) {
case NFC.SETTING_NFC_DEBUG:
DEBUG = result;
updateDebug();
break;
}
},
/**
* nsIObserver interface methods.
*/

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

@ -105,8 +105,11 @@ this.NFC_POWER_LEVEL_DISABLED = 0;
this.NFC_POWER_LEVEL_LOW = 1;
this.NFC_POWER_LEVEL_ENABLED = 2;
this.TOPIC_MOZSETTINGS_CHANGED = "mozsettings-changed";
this.TOPIC_XPCOM_SHUTDOWN = "xpcom-shutdown";
this.SETTING_NFC_DEBUG = "nfc.debugging.enabled";
this.NFC_PEER_EVENT_READY = 0x01;
this.NFC_PEER_EVENT_LOST = 0x02;

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

@ -27,12 +27,14 @@ interface nsINfcDOMEventTarget : nsISupports
void notifyPeerLost(in DOMString sessionToken);
};
[scriptable, uuid(d3f1bdc1-048f-44a8-abe2-bc386edce40b)]
[scriptable, uuid(cb9c934d-a7fa-422b-bcc1-4ac39741e6ec)]
interface nsINfcContentHelper : nsISupports
{
const long NFC_EVENT_PEER_READY = 0x01;
const long NFC_EVENT_PEER_LOST = 0x02;
void init(in nsIDOMWindow window);
boolean checkSessionToken(in DOMString sessionToken);
nsIDOMDOMRequest readNDEF(in nsIDOMWindow window, in DOMString sessionToken);

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

@ -148,6 +148,9 @@ mozNfc.prototype = {
init: function init(aWindow) {
debug("mozNfc init called");
this._window = aWindow;
if (this._nfcContentHelper) {
this._nfcContentHelper.init(aWindow);
}
},
// Only apps which have nfc-manager permission can call the following interfaces

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

@ -88,6 +88,7 @@
#include "mozilla/Preferences.h"
#include "nsFrameSelection.h"
#include "FrameLayerBuilder.h"
#include "mozilla/layers/AsyncPanZoomController.h"
#ifdef MOZ_XUL
#include "nsXULPopupManager.h"
@ -98,10 +99,6 @@
#include "nsTransitionManager.h"
#include "RestyleManager.h"
// Additional includes used on B2G by code in GetOrMaybeCreateDisplayPort().
#ifdef MOZ_WIDGET_GONK
#include "mozilla/layers/AsyncPanZoomController.h"
#endif
using namespace mozilla;
using namespace mozilla::dom;
@ -2734,9 +2731,6 @@ nsLayoutUtils::GetFramesForArea(nsIFrame* aFrame, const nsRect& aRect,
return NS_OK;
}
// This function is only used on B2G, and some compilers complain about
// unused static functions, so we need to #ifdef it.
#ifdef MOZ_WIDGET_GONK
// aScrollFrame and aScrollFrameAsScrollable must be non-nullptr
static FrameMetrics
CalculateFrameMetricsForDisplayPort(nsIFrame* aScrollFrame,
@ -2784,7 +2778,6 @@ CalculateFrameMetricsForDisplayPort(nsIFrame* aScrollFrame,
return metrics;
}
#endif
bool
nsLayoutUtils::GetOrMaybeCreateDisplayPort(nsDisplayListBuilder& aBuilder,
@ -2804,8 +2797,7 @@ nsLayoutUtils::GetOrMaybeCreateDisplayPort(nsDisplayListBuilder& aBuilder,
bool haveDisplayPort = GetDisplayPort(content, aOutDisplayport);
#ifdef MOZ_WIDGET_GONK
// On B2G, we perform an optimization where we ensure that at least one
// We perform an optimization where we ensure that at least one
// async-scrollable frame (i.e. one that WantsAsyncScroll()) has a displayport.
// If that's not the case yet, and we are async-scrollable, we will get a
// displayport.
@ -2834,7 +2826,6 @@ nsLayoutUtils::GetOrMaybeCreateDisplayPort(nsDisplayListBuilder& aBuilder,
// Record that the we now have a scrollable display port.
aBuilder.SetHaveScrollableDisplayPort();
}
#endif
return haveDisplayPort;
}

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

@ -297,60 +297,10 @@ pref("chrome.override_package.passwordmgr", "browser");
// enable xul error pages
pref("browser.xul.error_pages.enabled", true);
// Specify emptyRestriction = 0 so that bookmarks appear in the list by default
pref("browser.urlbar.default.behavior", 0);
pref("browser.urlbar.default.behavior.emptyRestriction", 0);
// Let the faviconservice know that we display favicons as 32x32px so that it
// uses the right size when optimizing favicons
pref("places.favicons.optimizeToDimension", 32);
// various and sundry awesomebar prefs (should remove/re-evaluate
// these once bug 447900 is fixed)
pref("browser.urlbar.clickSelectsAll", true);
pref("browser.urlbar.doubleClickSelectsAll", true);
pref("browser.urlbar.autoFill", false);
pref("browser.urlbar.matchOnlyTyped", false);
pref("browser.urlbar.matchBehavior", 1);
pref("browser.urlbar.filter.javascript", true);
pref("browser.urlbar.maxRichResults", 24); // increased so we see more results when portrait
pref("browser.urlbar.search.chunkSize", 1000);
pref("browser.urlbar.search.timeout", 100);
pref("browser.urlbar.restrict.history", "^");
pref("browser.urlbar.restrict.bookmark", "*");
pref("browser.urlbar.restrict.tag", "+");
pref("browser.urlbar.match.title", "#");
pref("browser.urlbar.match.url", "@");
pref("browser.urlbar.autocomplete.search_threshold", 5);
pref("browser.history.grouping", "day");
pref("browser.history.showSessions", false);
pref("browser.sessionhistory.max_entries", 50);
pref("browser.history_expire_sites", 40000);
pref("browser.places.migratePostDataAnnotations", true);
pref("browser.places.updateRecentTagsUri", true);
pref("places.frecency.numVisits", 10);
pref("places.frecency.numCalcOnIdle", 50);
pref("places.frecency.numCalcOnMigrate", 50);
pref("places.frecency.updateIdleTime", 60000);
pref("places.frecency.firstBucketCutoff", 4);
pref("places.frecency.secondBucketCutoff", 14);
pref("places.frecency.thirdBucketCutoff", 31);
pref("places.frecency.fourthBucketCutoff", 90);
pref("places.frecency.firstBucketWeight", 100);
pref("places.frecency.secondBucketWeight", 70);
pref("places.frecency.thirdBucketWeight", 50);
pref("places.frecency.fourthBucketWeight", 30);
pref("places.frecency.defaultBucketWeight", 10);
pref("places.frecency.embedVisitBonus", 0);
pref("places.frecency.linkVisitBonus", 100);
pref("places.frecency.typedVisitBonus", 2000);
pref("places.frecency.bookmarkVisitBonus", 150);
pref("places.frecency.downloadVisitBonus", 0);
pref("places.frecency.permRedirectVisitBonus", 0);
pref("places.frecency.tempRedirectVisitBonus", 0);
pref("places.frecency.defaultVisitBonus", 0);
pref("places.frecency.unvisitedBookmarkBonus", 140);
pref("places.frecency.unvisitedTypedBonus", 200);
// disable color management
pref("gfx.color_management.mode", 0);

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

@ -249,6 +249,37 @@ public class BrowserLocaleManager implements LocaleManager {
return changed;
}
/**
* Gecko needs to know the OS locale to compute a useful Accept-Language
* header. If it changed since last time, send a message to Gecko and
* persist the new value. If unchanged, returns immediately.
*
* @param prefs the SharedPreferences instance to use. Cannot be null.
* @param osLocale the new locale instance. Safe if null.
*/
public static void storeAndNotifyOSLocale(final SharedPreferences prefs,
final Locale osLocale) {
if (osLocale == null) {
return;
}
final String lastOSLocale = prefs.getString("osLocale", null);
final String osLocaleString = osLocale.toString();
if (osLocaleString.equals(lastOSLocale)) {
return;
}
// Store the Java-native form.
prefs.edit().putString("osLocale", osLocaleString).apply();
// The value we send to Gecko should be a language tag, not
// a Java locale string.
final String osLanguageTag = BrowserLocaleManager.getLanguageTag(osLocale);
final GeckoEvent localeOSEvent = GeckoEvent.createBroadcastEvent("Locale:OS", osLanguageTag);
GeckoAppShell.sendEventToGecko(localeOSEvent);
}
@Override
public String getAndApplyPersistedLocale(Context context) {
initialize(context);
@ -330,6 +361,9 @@ public class BrowserLocaleManager implements LocaleManager {
return GeckoSharedPrefs.forApp(context);
}
/**
* @return the persisted locale in Java format: "en_US".
*/
private String getPersistedLocale(Context context) {
final SharedPreferences settings = getSharedPreferences(context);
final String locale = settings.getString(PREF_LOCALE, "");
@ -364,6 +398,9 @@ public class BrowserLocaleManager implements LocaleManager {
* Returns the persisted locale if it differed.
*
* Does not notify Gecko.
*
* @param localeCode a locale string in Java format: "en_US".
* @return if it differed, a locale string in Java format: "en_US".
*/
private String updateLocale(Context context, String localeCode) {
// Fast path.
@ -377,6 +414,9 @@ public class BrowserLocaleManager implements LocaleManager {
return updateLocale(context, locale);
}
/**
* @return the Java locale string: e.g., "en_US".
*/
private String updateLocale(Context context, final Locale locale) {
// Fast path.
if (Locale.getDefault().equals(locale)) {

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

@ -1213,6 +1213,9 @@ public abstract class GeckoApp
// the UI.
// This is using a sledgehammer to crack a nut, but it'll do for
// now.
// Our OS locale pref will be detected as invalid after the
// restart, and will be propagated to Gecko accordingly, so there's
// no need to touch that here.
if (BrowserLocaleManager.getInstance().systemLocaleDidChange()) {
Log.i(LOGTAG, "System locale changed. Restarting.");
doRestart();
@ -1324,28 +1327,36 @@ public abstract class GeckoApp
final String profilePath = getProfile().getDir().getAbsolutePath();
final EventDispatcher dispatcher = EventDispatcher.getInstance();
final String osLocale = Locale.getDefault().toString();
String appLocale = localeManager.getAndApplyPersistedLocale(GeckoApp.this);
Log.d(LOGTAG, "OS locale is " + osLocale + ", app locale is " + appLocale);
// This is the locale prior to fixing it up.
final Locale osLocale = Locale.getDefault();
if (appLocale == null) {
appLocale = osLocale;
// Both of these are Java-format locale strings: "en_US", not "en-US".
final String osLocaleString = osLocale.toString();
String appLocaleString = localeManager.getAndApplyPersistedLocale(GeckoApp.this);
Log.d(LOGTAG, "OS locale is " + osLocaleString + ", app locale is " + appLocaleString);
if (appLocaleString == null) {
appLocaleString = osLocaleString;
}
mHealthRecorder = GeckoApp.this.createHealthRecorder(GeckoApp.this,
profilePath,
dispatcher,
osLocale,
appLocale,
osLocaleString,
appLocaleString,
previousSession);
final String uiLocale = appLocale;
final String uiLocale = appLocaleString;
ThreadUtils.postToUiThread(new Runnable() {
@Override
public void run() {
GeckoApp.this.onLocaleReady(uiLocale);
}
});
// We use per-profile prefs here, because we're tracking against
// a Gecko pref. The same applies to the locale switcher!
BrowserLocaleManager.storeAndNotifyOSLocale(GeckoSharedPrefs.forProfile(GeckoApp.this), osLocale);
}
});
@ -1853,7 +1864,7 @@ public abstract class GeckoApp
}
}
/*
/**
* Handles getting a URI from an intent in a way that is backwards-
* compatible with our previous implementations.
*/
@ -1863,18 +1874,7 @@ public abstract class GeckoApp
return null;
}
String uri = intent.getDataString();
if (uri != null) {
return uri;
}
if ((action != null && action.startsWith(ACTION_WEBAPP_PREFIX)) || ACTION_HOMESCREEN_SHORTCUT.equals(action)) {
uri = StringUtils.getStringExtra(intent, "args");
if (uri != null && uri.startsWith("--url=")) {
uri.replace("--url=", "");
}
}
return uri;
return intent.getDataString();
}
protected int getOrientation() {

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

@ -201,10 +201,6 @@ public class GLController {
return mServerSurfaceValid;
}
public boolean isCompositorCreated() {
return mCompositorCreated;
}
private void initEGL() {
if (mEGL != null) {
return;

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

@ -476,13 +476,8 @@ public class LayerView extends FrameLayout implements Tabs.OnTabsChangedListener
* TextureView instead of a SurfaceView, the first phase is skipped.
*/
private void onSizeChanged(int width, int height) {
if (!mGLController.isCompositorCreated()) {
return;
}
surfaceChanged(width, height);
if (mSurfaceView == null) {
if (!mGLController.isServerSurfaceValid() || mSurfaceView == null) {
surfaceChanged(width, height);
return;
}

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

@ -82,6 +82,16 @@ abstract class BaseTest extends BaseRobocopTest {
protected int mScreenMidHeight;
private final HashSet<Integer> mKnownTabIDs = new HashSet<Integer>();
protected void blockForDelayedStartup() {
try {
Actions.EventExpecter delayedStartupExpector = mActions.expectGeckoEvent("Gecko:DelayedStartup");
delayedStartupExpector.blockForEvent(GECKO_READY_WAIT_MS, true);
delayedStartupExpector.unregisterListener();
} catch (Exception e) {
mAsserter.dumpLog("Exception in blockForDelayedStartup", e);
}
}
protected void blockForGeckoReady() {
try {
Actions.EventExpecter geckoReadyExpector = mActions.expectGeckoEvent("Gecko:Ready");
@ -127,14 +137,17 @@ abstract class BaseTest extends BaseRobocopTest {
throwIfScreenNotOn();
}
protected void initializeProfile() {
final GeckoProfile profile;
protected GeckoProfile getTestProfile() {
if (mProfile.startsWith("/")) {
profile = GeckoProfile.get(getActivity(), "default", mProfile);
} else {
profile = GeckoProfile.get(getActivity(), mProfile);
return GeckoProfile.get(getActivity(), "default", mProfile);
}
return GeckoProfile.get(getActivity(), mProfile);
}
protected void initializeProfile() {
final GeckoProfile profile = getTestProfile();
// In Robocop tests, we typically don't get initialized correctly, because
// GeckoProfile doesn't create the profile directory.
profile.enqueueInitialization(profile.getDir());

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

@ -21,11 +21,14 @@ public class JavascriptTest extends BaseTest {
public void testJavascript() throws Exception {
blockForGeckoReady();
doTestJavascript();
}
protected void doTestJavascript() throws Exception {
// We want to be waiting for Robocop messages before the page is loaded
// because the test harness runs each test in the suite (and possibly
// completes testing) before the page load event is fired.
final Actions.EventExpecter expecter =
mActions.expectGeckoEvent(EVENT_TYPE);
final Actions.EventExpecter expecter = mActions.expectGeckoEvent(EVENT_TYPE);
mAsserter.dumpLog("Registered listener for " + EVENT_TYPE);
final String url = getAbsoluteUrl(StringHelper.getHarnessUrlForJavascript(javascriptUrl));

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

@ -104,6 +104,7 @@ skip-if = android_version == "10"
[testJNI]
# [testMozPay] # see bug 945675
[testOrderedBroadcast]
[testOSLocale]
[testResourceSubstitutions]
[testRestrictedProfiles]
[testSharedPreferences]

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

@ -0,0 +1,134 @@
/* 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.tests;
import java.util.Locale;
import org.mozilla.gecko.BrowserLocaleManager;
import org.mozilla.gecko.GeckoSharedPrefs;
import org.mozilla.gecko.PrefsHelper;
import android.content.SharedPreferences;
public class testOSLocale extends BaseTest {
@Override
public void setUp() throws Exception {
super.setUp();
// Clear per-profile SharedPreferences as a workaround for Bug 1069687.
// We're trying to exercise logic that only applies on first onCreate!
// We can't rely on this occurring prior to the first broadcast, though,
// so see the main test method for more logic.
final String profileName = getTestProfile().getName();
mAsserter.info("Setup", "Clearing pref in " + profileName + ".");
GeckoSharedPrefs.forProfileName(getActivity(), profileName)
.edit()
.remove("osLocale")
.apply();
}
public static class PrefState extends PrefsHelper.PrefHandlerBase {
private static final String PREF_LOCALE_OS = "intl.locale.os";
private static final String PREF_ACCEPT_LANG = "intl.accept_languages";
private static final String[] TO_FETCH = {PREF_LOCALE_OS, PREF_ACCEPT_LANG};
public volatile String osLocale;
public volatile String acceptLanguages;
private final Object waiter = new Object();
public void fetch() throws InterruptedException {
synchronized (waiter) {
PrefsHelper.getPrefs(TO_FETCH, this);
waiter.wait(MAX_WAIT_MS);
}
}
@Override
public void prefValue(String pref, String value) {
switch (pref) {
case PREF_LOCALE_OS:
osLocale = value;
return;
case PREF_ACCEPT_LANG:
acceptLanguages = value;
return;
}
}
@Override
public void finish() {
synchronized (waiter) {
waiter.notify();
}
}
}
public void testOSLocale() throws Exception {
blockForDelayedStartup();
final SharedPreferences prefs = GeckoSharedPrefs.forProfile(getActivity());
final PrefState state = new PrefState();
state.fetch();
// We don't know at this point whether we were run against a dirty profile or not.
//
// If we cleared the pref above prior to BrowserApp's delayed init, or our Gecko
// profile has been used before, then we're already going to be set up for en-US.
//
// If we cleared the pref after the initial broadcast, and our Android-side profile
// has been used before but the Gecko profile is clean, then the Gecko prefs won't
// have been set.
//
// Instead, we always send a new locale code, and see what we get.
final Locale fr = BrowserLocaleManager.parseLocaleCode("fr");
BrowserLocaleManager.storeAndNotifyOSLocale(prefs, fr);
state.fetch();
mAsserter.is(state.osLocale, "fr", "We're in fr.");
// Now we can see what the expected Accept-Languages header should be.
// The OS locale is 'fr', so we have our app locale (en-US),
// the OS locale (fr), then any remaining fallbacks from intl.properties.
mAsserter.is(state.acceptLanguages, "en-us,fr,en", "We have the default en-US+fr Accept-Languages.");
// Now set the app locale to be es-ES.
BrowserLocaleManager.getInstance().setSelectedLocale(getActivity(), "es-ES");
state.fetch();
mAsserter.is(state.osLocale, "fr", "We're still in fr.");
// The correct set here depends on whether the
// browser was built with multiple locales or not.
// This is exasperating, but hey.
final boolean isMultiLocaleBuild = false;
// This never changes.
final String SELECTED_LOCALES = "es-es,fr,";
// Expected, from es-ES's intl.properties:
final String EXPECTED = SELECTED_LOCALES +
(isMultiLocaleBuild ? "es,en-us,en" : // Expected, from es-ES's intl.properties.
"en-us,en"); // Expected, from en-US (the default).
mAsserter.is(state.acceptLanguages, EXPECTED, "We have the right es-ES+fr Accept-Languages for this build.");
// And back to en-US.
final Locale en_US = BrowserLocaleManager.parseLocaleCode("en-US");
BrowserLocaleManager.storeAndNotifyOSLocale(prefs, en_US);
BrowserLocaleManager.getInstance().resetToSystemLocale(getActivity());
state.fetch();
mAsserter.is(state.osLocale, "en-US", "We're in en-US.");
mAsserter.is(state.acceptLanguages, "en-us,en", "We have the default processed en-US Accept-Languages.");
}
}

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

@ -67,7 +67,9 @@ function init() {
}
let sumoLink = Services.urlFormatter.formatURLPref("app.support.baseURL");
document.getElementById("sumo-link").href = sumoLink;
document.getElementById("help-section").addEventListener("click", function() {
window.open(sumoLink, "_blank");
}, false);
window.addEventListener("popstate", function (aEvent) {
updateActiveSection(aEvent.state ? aEvent.state.section : "intro")

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

@ -82,7 +82,7 @@
<footer>
<div id="help-section">
<img id="sumo-icon" />
<span>&support.pre3;<a id="sumo-link">&support.link2;</a>&support.post3;</span>
<span>&support.pre3;<span class="link">&support.link2;</span>&support.post3;</span>
</div>
</footer>
<script type="application/javascript;version=1.8" src="chrome://browser/content/aboutFeedback.js"></script>

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

@ -342,6 +342,7 @@ var BrowserApp = {
Services.androidBridge.browserApp = this;
Services.obs.addObserver(this, "Locale:OS", false);
Services.obs.addObserver(this, "Locale:Changed", false);
Services.obs.addObserver(this, "Tab:Load", false);
Services.obs.addObserver(this, "Tab:Selected", false);
@ -1758,6 +1759,34 @@ var BrowserApp = {
WebappManager.autoUninstall(JSON.parse(aData));
break;
case "Locale:OS":
// We know the system locale. We use this for generating Accept-Language headers.
console.log("Locale:OS: " + aData);
let currentOSLocale;
try {
currentOSLocale = Services.prefs.getCharPref("intl.locale.os");
} catch (e) {
}
if (currentOSLocale == aData) {
break;
}
console.log("New OS locale.");
// Ensure that this choice is immediately persisted, because
// Gecko won't be told again if it forgets.
Services.prefs.setCharPref("intl.locale.os", aData);
Services.prefs.savePrefFile(null);
let appLocale;
try {
appLocale = Services.prefs.getCharPref("general.useragent.locale");
} catch (e) {
}
this.computeAcceptLanguages(aData, appLocale);
break;
case "Locale:Changed":
if (aData) {
// The value provided to Locale:Changed should be a BCP47 language tag
@ -1779,6 +1808,16 @@ var BrowserApp = {
// Blow away the string cache so that future lookups get the
// correct locale.
Services.strings.flushBundles();
// Make sure we use the right Accept-Language header.
let osLocale;
try {
// This should never not be set at this point, but better safe than sorry.
osLocale = Services.prefs.getCharPref("intl.locale.os");
} catch (e) {
}
this.computeAcceptLanguages(osLocale, aData);
break;
default:
@ -1788,6 +1827,63 @@ var BrowserApp = {
}
},
/**
* Set intl.accept_languages accordingly.
*
* After Bug 881510 this will also accept a real Accept-Language choice as
* input; all Accept-Language logic lives here.
*
* osLocale should never be null, but this method is safe regardless.
* appLocale may explicitly be null.
*/
computeAcceptLanguages(osLocale, appLocale) {
let defaultBranch = Services.prefs.getDefaultBranch(null);
let defaultAccept = defaultBranch.getComplexValue("intl.accept_languages", Ci.nsIPrefLocalizedString).data;
console.log("Default intl.accept_languages = " + defaultAccept);
// A guard for potential breakage. Bug 438031.
// This should not be necessary, because we're reading from the default branch,
// but better safe than sorry.
if (defaultAccept && defaultAccept.startsWith("chrome://")) {
defaultAccept = null;
} else {
// Ensure lowercase everywhere so we can compare.
defaultAccept = defaultAccept.toLowerCase();
}
if (appLocale) {
appLocale = appLocale.toLowerCase();
}
if (osLocale) {
osLocale = osLocale.toLowerCase();
}
// Eliminate values if they're present in the default.
let chosen;
if (defaultAccept) {
// intl.accept_languages is a comma-separated list, with no q-value params. Those
// are added when the header is generated.
chosen = defaultAccept.split(",")
.map(String.trim)
.filter((x) => (x != appLocale && x != osLocale));
} else {
chosen = [];
}
if (osLocale) {
chosen.unshift(osLocale);
}
if (appLocale && appLocale != osLocale) {
chosen.unshift(appLocale);
}
let result = chosen.join(",");
console.log("Setting intl.accept_languages to " + result);
Services.prefs.setCharPref("intl.accept_languages", result);
},
get defaultBrowserWidth() {
delete this.defaultBrowserWidth;
let width = Services.prefs.getIntPref("browser.viewport.desktopWidth");

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

@ -190,8 +190,9 @@ footer {
color: #fff;
}
#sumo-link {
.link {
color: #222;
text-decoration: underline;
}
@media screen and (max-height: 400px) {

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

@ -17,22 +17,38 @@ SimpleTest.waitForExplicitFinish();
var usernameInputFired = false;
var passwordInputFired = false;
var usernameChangeFired = false;
var passwordChangeFired = false;
var onloadFired = false;
function onNewEvent(e) {
info("Got " + e.type + " event.");
if (e.type == "load") {
onloadFired = true;
} else if (e.target.name == "uname") {
ise(e.target.value, "testuser", "Should get 'testuser' as username");
ok(!usernameInputFired, "Should not have gotten an input event for the username field yet.");
usernameInputFired = true;
} else if (e.target.name == "pword") {
ise(e.target.value, "testpass", "Should get 'testpass' as password");
ok(!passwordInputFired, "Should not have gotten an input event for the password field yet.");
passwordInputFired = true;
} else if (e.type == "input") {
if (e.target.name == "uname") {
ise(e.target.value, "testuser", "Should get 'testuser' as username");
ok(!usernameInputFired, "Should not have gotten an input event for the username field yet.");
usernameInputFired = true;
} else if (e.target.name == "pword") {
ise(e.target.value, "testpass", "Should get 'testpass' as password");
ok(!passwordInputFired, "Should not have gotten an input event for the password field yet.");
passwordInputFired = true;
}
} else if (e.type == "change") {
if (e.target.name == "uname") {
ise(e.target.value, "testuser", "Should get 'testuser' as username");
ok(usernameInputFired, "Should get input event before change event for username field.");
ok(!usernameChangeFired, "Should not have gotten a change event for the username field yet.");
usernameChangeFired = true;
} else if (e.target.name == "pword") {
ise(e.target.value, "testpass", "Should get 'testpass' as password");
ok(passwordInputFired, "Should get input event before change event for password field.");
ok(!passwordChangeFired, "Should not have gotten a change event for the password field yet.");
passwordChangeFired = true;
}
}
if (onloadFired && usernameInputFired && passwordInputFired) {
if (onloadFired && usernameInputFired && passwordInputFired && usernameChangeFired && passwordChangeFired) {
ok(true, "All events fired as expected, we're done.");
SimpleTest.finish();
}
@ -42,12 +58,16 @@ SimpleTest.registerCleanupFunction(function cleanup() {
clearTimeout(timeout);
$_(1, "uname").removeAttribute("oninput");
$_(1, "pword").removeAttribute("oninput");
$_(1, "uname").removeAttribute("onchange");
$_(1, "pword").removeAttribute("onchange");
document.body.removeAttribute("onload");
});
var timeout = setTimeout(function() {
ok(usernameInputFired, "Username input event should have fired by now.");
ok(passwordInputFired, "Password input event should have fired by now.");
ok(usernameChangeFired, "Username change event should have fired by now.");
ok(passwordChangeFired, "Password change event should have fired by now.");
ok(onloadFired, "Window load event should have fired by now.");
ok(false, "Not all events fired yet.");
SimpleTest.finish();
@ -61,8 +81,8 @@ var timeout = setTimeout(function() {
<form id="form1" action="formtest.js">
<p>This is form 1.</p>
<input type="text" name="uname" oninput="onNewEvent(event)">
<input type="password" name="pword" oninput="onNewEvent(event)">
<input type="text" name="uname" oninput="onNewEvent(event)" onchange="onNewEvent(event)">
<input type="password" name="pword" oninput="onNewEvent(event)" onchange="onNewEvent(event)">
<button type="submit">Submit</button>
<button type="reset"> Reset </button>

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

@ -208,35 +208,8 @@ this.PlacesUtils = {
}
},
/**
* Cache array of read-only item IDs.
*
* The first time this property is called:
* - the cache is filled with all ids with the RO annotation
* - an annotation observer is added
* - a shutdown observer is added
*
* When the annotation observer detects annotations added or
* removed that are the RO annotation name, it adds/removes
* the ids from the cache.
*
* At shutdown, the annotation and shutdown observers are removed.
*/
get _readOnly() {
// Add annotations observer.
this.annotations.addObserver(this, false);
this.registerShutdownFunction(function () {
this.annotations.removeObserver(this);
});
var readOnly = this.annotations.getItemsWithAnnotation(this.READ_ONLY_ANNO);
this.__defineGetter__("_readOnly", function() readOnly);
return this._readOnly;
},
QueryInterface: XPCOMUtils.generateQI([
Ci.nsIAnnotationObserver
, Ci.nsIObserver
Ci.nsIObserver
, Ci.nsITransactionListener
]),
@ -275,24 +248,6 @@ this.PlacesUtils = {
}
},
//////////////////////////////////////////////////////////////////////////////
//// nsIAnnotationObserver
onItemAnnotationSet: function PU_onItemAnnotationSet(aItemId, aAnnotationName)
{
if (aAnnotationName == this.READ_ONLY_ANNO &&
this._readOnly.indexOf(aItemId) == -1)
this._readOnly.push(aItemId);
},
onItemAnnotationRemoved:
function PU_onItemAnnotationRemoved(aItemId, aAnnotationName)
{
var index = this._readOnly.indexOf(aItemId);
if (aAnnotationName == this.READ_ONLY_ANNO && index > -1)
delete this._readOnly[index];
},
onPageAnnotationSet: function() {},
onPageAnnotationRemoved: function() {},
@ -342,27 +297,6 @@ this.PlacesUtils = {
willMerge: function PU_willMerge() {},
didMerge: function PU_didMerge() {},
/**
* Determines if a node is read only (children cannot be inserted, sometimes
* they cannot be removed depending on the circumstance)
* @param aNode
* A result node
* @returns true if the node is readonly, false otherwise
*/
nodeIsReadOnly: function PU_nodeIsReadOnly(aNode) {
let itemId = aNode.itemId;
if (itemId != -1) {
return this._readOnly.indexOf(itemId) != -1;
}
if (this.nodeIsQuery(aNode) &&
asQuery(aNode).queryOptions.resultType !=
Ci.nsINavHistoryQueryOptions.RESULTS_AS_TAG_CONTENTS)
return aNode.childrenReadOnly;
return false;
},
/**
* Determines whether or not a ResultNode is a host container.
* @param aNode
@ -433,17 +367,6 @@ this.PlacesUtils = {
this.nodeIsHost(aNode));
},
/**
* Determines whether or not a node is a readonly folder.
* @param aNode
* The node to test.
* @returns true if the node is a readonly folder.
*/
isReadonlyFolder: function(aNode) {
return this.nodeIsFolder(aNode) &&
this._readOnly.indexOf(asQuery(aNode).folderItemId) != -1;
},
/**
* Gets the concrete item-id for the given node. Generally, this is just
* node.itemId, but for folder-shortcuts that's node.folderItemId.
@ -1113,10 +1036,9 @@ this.PlacesUtils = {
if (guid) {
aJSNode.itemGuid = guid;
var parent = aPlacesNode.parent;
if (parent) {
if (parent)
aJSNode.parent = parent.itemId;
aJSNode.parentReadOnly = PlacesUtils.nodeIsReadOnly(parent);
}
var dateAdded = aPlacesNode.dateAdded;
if (dateAdded)
aJSNode.dateAdded = dateAdded;

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

@ -223,7 +223,7 @@ interface nsINavBookmarkObserver : nsISupports
* folders. A URI in history can be contained in one or more such folders.
*/
[scriptable, uuid(A78EA368-E28E-462E-897A-26606D4DDCE6)]
[scriptable, uuid(4C309044-B6DA-4511-AF57-E8940DB00045)]
interface nsINavBookmarksService : nsISupports
{
/**
@ -466,30 +466,6 @@ interface nsINavBookmarksService : nsISupports
*/
unsigned short getItemType(in long long aItemId);
/**
* Checks whether a folder is marked as read-only.
* If this is set to true, UI will not allow the user to add, remove,
* or reorder children in this folder. The default for all folders is false.
* Note: This does not restrict API calls, only UI actions.
*
* @param aItemId
* the item-id of the folder.
*/
boolean getFolderReadonly(in long long aItemId);
/**
* Sets or unsets the readonly flag from a folder.
* If this is set to true, UI will not allow the user to add, remove,
* or reorder children in this folder. The default for all folders is false.
* Note: This does not restrict API calls, only UI actions.
*
* @param aFolder
* the item-id of the folder.
* @param aReadOnly
* the read-only state (boolean).
*/
void setFolderReadonly(in long long aFolder, in boolean aReadOnly);
/**
* Returns true if the given URI is in any bookmark folder. If you want the
* results to be redirect-aware, use getBookmarkedURIFor()

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

@ -175,7 +175,7 @@ interface nsINavHistoryResultNode : nsISupports
* Bookmark folders and places queries will be QueryResultNodes which extends
* these items.
*/
[scriptable, uuid(5bac9734-c0ff-44eb-8d19-da88462ff6da)]
[scriptable, uuid(3E9CC95F-0D93-45F1-894F-908EEB9866D7)]
interface nsINavHistoryContainerResultNode : nsINavHistoryResultNode
{
@ -256,15 +256,6 @@ interface nsINavHistoryContainerResultNode : nsINavHistoryResultNode
in PRTime aTime,
in long long aItemId,
in boolean aRecursive);
/**
* Returns false if this node's list of children can be modified
* (adding or removing children, or reordering children), or true if
* the UI should not allow the list of children to be modified.
* This is false for bookmark folder nodes unless setFolderReadOnly() has
* been called to override it, and true for non-folder nodes.
*/
readonly attribute boolean childrenReadOnly;
};
@ -275,7 +266,7 @@ interface nsINavHistoryContainerResultNode : nsINavHistoryResultNode
* generated this node, this item will report it has no children and never try
* to populate itself.
*/
[scriptable, uuid(a4144c3e-8125-46d5-a719-831bec8095f4)]
[scriptable, uuid(91AC5E59-3F5C-4ACD-AB3B-325FC425A5A1)]
interface nsINavHistoryQueryResultNode : nsINavHistoryContainerResultNode
{
/**
@ -1118,12 +1109,9 @@ interface nsINavHistoryQueryOptions : nsISupports
attribute boolean excludeQueries;
/**
* Set to true to exclude read-only folders from the query results. This is
* designed for cases where you want to give the user the option of filing
* something into a list of folders. It only affects cases where the actual
* folder result node would appear in its parent folder and filters it out.
* It doesn't affect the query at all, and doesn't affect more complex
* queries (such as "folders with annotation X").
* DO NOT USE THIS API. IT'LL BE REMOVED IN BUG 1072833.
*
* Set to true to exclude live bookmarks from the query results.
*/
attribute boolean excludeReadOnlyFolders;

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

@ -547,7 +547,6 @@ function Livemark(aLivemarkInfo)
aLivemarkInfo.title,
aLivemarkInfo.index,
aLivemarkInfo.guid);
PlacesUtils.bookmarks.setFolderReadonly(this.id, true);
this.writeFeedURI(aLivemarkInfo.feedURI);
if (aLivemarkInfo.siteURI) {
this.writeSiteURI(aLivemarkInfo.siteURI);

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

@ -60,7 +60,7 @@ PLACES_FACTORY_SINGLETON_IMPLEMENTATION(nsNavBookmarks, gBookmarksService)
#define BOOKMARKS_ANNO_PREFIX "bookmarks/"
#define BOOKMARKS_TOOLBAR_FOLDER_ANNO NS_LITERAL_CSTRING(BOOKMARKS_ANNO_PREFIX "toolbarFolder")
#define READ_ONLY_ANNO NS_LITERAL_CSTRING("placesInternal/READ_ONLY")
#define FEED_URI_ANNO NS_LITERAL_CSTRING("livemark/feedURI")
namespace {
@ -790,46 +790,18 @@ nsNavBookmarks::CreateFolder(int64_t aParent, const nsACString& aName,
return NS_OK;
}
NS_IMETHODIMP
nsNavBookmarks::GetFolderReadonly(int64_t aFolder, bool* aResult)
bool nsNavBookmarks::IsLivemark(int64_t aFolderId)
{
NS_ENSURE_ARG_MIN(aFolder, 1);
NS_ENSURE_ARG_POINTER(aResult);
nsAnnotationService* annosvc = nsAnnotationService::GetAnnotationService();
NS_ENSURE_TRUE(annosvc, NS_ERROR_OUT_OF_MEMORY);
nsresult rv = annosvc->ItemHasAnnotation(aFolder, READ_ONLY_ANNO, aResult);
NS_ENSURE_SUCCESS(rv, rv);
return NS_OK;
NS_ENSURE_TRUE(annosvc, false);
bool isLivemark;
nsresult rv = annosvc->ItemHasAnnotation(aFolderId,
FEED_URI_ANNO,
&isLivemark);
NS_ENSURE_SUCCESS(rv, false);
return isLivemark;
}
NS_IMETHODIMP
nsNavBookmarks::SetFolderReadonly(int64_t aFolder, bool aReadOnly)
{
NS_ENSURE_ARG_MIN(aFolder, 1);
nsAnnotationService* annosvc = nsAnnotationService::GetAnnotationService();
NS_ENSURE_TRUE(annosvc, NS_ERROR_OUT_OF_MEMORY);
nsresult rv;
if (aReadOnly) {
rv = annosvc->SetItemAnnotationInt32(aFolder, READ_ONLY_ANNO, 1, 0,
nsAnnotationService::EXPIRE_NEVER);
NS_ENSURE_SUCCESS(rv, rv);
}
else {
bool hasAnno;
rv = annosvc->ItemHasAnnotation(aFolder, READ_ONLY_ANNO, &hasAnno);
NS_ENSURE_SUCCESS(rv, rv);
if (hasAnno) {
rv = annosvc->RemoveItemAnnotation(aFolder, READ_ONLY_ANNO);
NS_ENSURE_SUCCESS(rv, rv);
}
}
return NS_OK;
}
nsresult
nsNavBookmarks::CreateContainerWithID(int64_t aItemId,
int64_t aParent,
@ -1866,11 +1838,10 @@ nsNavBookmarks::ProcessFolderNodeRow(
}
}
else if (itemType == TYPE_FOLDER) {
// ExcludeReadOnlyFolders currently means "ExcludeLivemarks" (to be fixed in
// bug 1072833)
if (aOptions->ExcludeReadOnlyFolders()) {
// If the folder is read-only, skip it.
bool readOnly = false;
GetFolderReadonly(id, &readOnly);
if (readOnly)
if (IsLivemark(id))
return NS_OK;
}

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

@ -234,6 +234,15 @@ private:
~nsNavBookmarks();
/**
* Checks whether or not aFolderId points to a live bookmark.
*
* @param aFolderId
* the item-id of the folder to check.
* @return true if aFolderId points to live bookmarks, false otherwise.
*/
bool IsLivemark(int64_t aFolderId);
/**
* Locates the root items in the bookmarks folder hierarchy assigning folder
* ids to the root properties that are exposed through the service interface.

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

@ -322,13 +322,12 @@ NS_INTERFACE_MAP_END_INHERITING(nsNavHistoryResultNode)
nsNavHistoryContainerResultNode::nsNavHistoryContainerResultNode(
const nsACString& aURI, const nsACString& aTitle,
const nsACString& aIconURI, uint32_t aContainerType, bool aReadOnly,
const nsACString& aIconURI, uint32_t aContainerType,
nsNavHistoryQueryOptions* aOptions) :
nsNavHistoryResultNode(aURI, aTitle, 0, 0, aIconURI),
mResult(nullptr),
mContainerType(aContainerType),
mExpanded(false),
mChildrenReadOnly(aReadOnly),
mOptions(aOptions),
mAsyncCanceledState(NOT_CANCELED)
{
@ -337,13 +336,12 @@ nsNavHistoryContainerResultNode::nsNavHistoryContainerResultNode(
nsNavHistoryContainerResultNode::nsNavHistoryContainerResultNode(
const nsACString& aURI, const nsACString& aTitle,
PRTime aTime,
const nsACString& aIconURI, uint32_t aContainerType, bool aReadOnly,
const nsACString& aIconURI, uint32_t aContainerType,
nsNavHistoryQueryOptions* aOptions) :
nsNavHistoryResultNode(aURI, aTitle, 0, aTime, aIconURI),
mResult(nullptr),
mContainerType(aContainerType),
mExpanded(false),
mChildrenReadOnly(aReadOnly),
mOptions(aOptions),
mAsyncCanceledState(NOT_CANCELED)
{
@ -1716,16 +1714,6 @@ nsNavHistoryContainerResultNode::FindNodeByDetails(const nsACString& aURIString,
return NS_OK;
}
/**
* @note Overridden for folders to query the bookmarks service directly.
*/
NS_IMETHODIMP
nsNavHistoryContainerResultNode::GetChildrenReadOnly(bool *aChildrenReadOnly)
{
*aChildrenReadOnly = mChildrenReadOnly;
return NS_OK;
}
/**
* HOW QUERY UPDATING WORKS
*
@ -1753,7 +1741,7 @@ nsNavHistoryQueryResultNode::nsNavHistoryQueryResultNode(
const nsACString& aQueryURI) :
nsNavHistoryContainerResultNode(aQueryURI, aTitle, aIconURI,
nsNavHistoryResultNode::RESULT_TYPE_QUERY,
true, nullptr),
nullptr),
mLiveUpdate(QUERYUPDATE_COMPLEX_WITH_BOOKMARKS),
mHasSearchTerms(false),
mContentsValid(false),
@ -1767,7 +1755,7 @@ nsNavHistoryQueryResultNode::nsNavHistoryQueryResultNode(
nsNavHistoryQueryOptions* aOptions) :
nsNavHistoryContainerResultNode(EmptyCString(), aTitle, aIconURI,
nsNavHistoryResultNode::RESULT_TYPE_QUERY,
true, aOptions),
aOptions),
mQueries(aQueries),
mContentsValid(false),
mBatchChanges(0),
@ -1800,7 +1788,7 @@ nsNavHistoryQueryResultNode::nsNavHistoryQueryResultNode(
nsNavHistoryQueryOptions* aOptions) :
nsNavHistoryContainerResultNode(EmptyCString(), aTitle, aTime, aIconURI,
nsNavHistoryResultNode::RESULT_TYPE_QUERY,
true, aOptions),
aOptions),
mQueries(aQueries),
mContentsValid(false),
mBatchChanges(0),
@ -2988,7 +2976,7 @@ nsNavHistoryFolderResultNode::nsNavHistoryFolderResultNode(
int64_t aFolderId) :
nsNavHistoryContainerResultNode(EmptyCString(), aTitle, EmptyCString(),
nsNavHistoryResultNode::RESULT_TYPE_FOLDER,
false, aOptions),
aOptions),
mContentsValid(false),
mQueryItemId(-1),
mIsRegisteredFolderObserver(false)
@ -3090,25 +3078,6 @@ nsNavHistoryFolderResultNode::GetItemId(int64_t* aItemId)
return NS_OK;
}
/**
* Here, we override the getter and ignore the value stored in our object.
* The bookmarks service can tell us whether this folder should be read-only
* or not.
*
* It would be nice to put this code in the folder constructor, but the
* database was complaining. I believe it is because most folders are created
* while enumerating the bookmarks table and having a statement open, and doing
* another statement might make it unhappy in some cases.
*/
NS_IMETHODIMP
nsNavHistoryFolderResultNode::GetChildrenReadOnly(bool *aChildrenReadOnly)
{
nsNavBookmarks* bookmarks = nsNavBookmarks::GetBookmarksService();
NS_ENSURE_TRUE(bookmarks, NS_ERROR_UNEXPECTED);
return bookmarks->GetFolderReadonly(mItemId, aChildrenReadOnly);
}
NS_IMETHODIMP
nsNavHistoryFolderResultNode::GetFolderItemId(int64_t* aItemId)
{

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

@ -401,7 +401,7 @@ NS_DEFINE_STATIC_IID_ACCESSOR(nsNavHistoryResultNode, NS_NAVHISTORYRESULTNODE_II
// derived classes each provide their own implementation of has children and
// forward the rest to us using this macro
#define NS_FORWARD_CONTAINERNODE_EXCEPT_HASCHILDREN_AND_READONLY \
#define NS_FORWARD_CONTAINERNODE_EXCEPT_HASCHILDREN \
NS_IMETHOD GetState(uint16_t* _state) \
{ return nsNavHistoryContainerResultNode::GetState(_state); } \
NS_IMETHOD GetContainerOpen(bool *aContainerOpen) \
@ -430,12 +430,12 @@ public:
nsNavHistoryContainerResultNode(
const nsACString& aURI, const nsACString& aTitle,
const nsACString& aIconURI, uint32_t aContainerType,
bool aReadOnly, nsNavHistoryQueryOptions* aOptions);
nsNavHistoryQueryOptions* aOptions);
nsNavHistoryContainerResultNode(
const nsACString& aURI, const nsACString& aTitle,
PRTime aTime,
const nsACString& aIconURI, uint32_t aContainerType,
bool aReadOnly, nsNavHistoryQueryOptions* aOptions);
nsNavHistoryQueryOptions* aOptions);
virtual nsresult Refresh();
@ -479,8 +479,6 @@ public:
// Filled in by the result type generator in nsNavHistory.
nsCOMArray<nsNavHistoryResultNode> mChildren;
bool mChildrenReadOnly;
nsCOMPtr<nsNavHistoryQueryOptions> mOptions;
void FillStats();
@ -644,10 +642,8 @@ public:
NS_IMETHOD GetType(uint32_t* type)
{ *type = nsNavHistoryResultNode::RESULT_TYPE_QUERY; return NS_OK; }
NS_IMETHOD GetUri(nsACString& aURI); // does special lazy creation
NS_FORWARD_CONTAINERNODE_EXCEPT_HASCHILDREN_AND_READONLY
NS_FORWARD_CONTAINERNODE_EXCEPT_HASCHILDREN
NS_IMETHOD GetHasChildren(bool* aHasChildren);
NS_IMETHOD GetChildrenReadOnly(bool *aChildrenReadOnly)
{ return nsNavHistoryContainerResultNode::GetChildrenReadOnly(aChildrenReadOnly); }
NS_DECL_NSINAVHISTORYQUERYRESULTNODE
bool CanExpand();
@ -725,9 +721,8 @@ public:
return NS_OK;
}
NS_IMETHOD GetUri(nsACString& aURI);
NS_FORWARD_CONTAINERNODE_EXCEPT_HASCHILDREN_AND_READONLY
NS_FORWARD_CONTAINERNODE_EXCEPT_HASCHILDREN
NS_IMETHOD GetHasChildren(bool* aHasChildren);
NS_IMETHOD GetChildrenReadOnly(bool *aChildrenReadOnly);
NS_IMETHOD GetItemId(int64_t *aItemId);
NS_DECL_NSINAVHISTORYQUERYRESULTNODE

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

@ -152,8 +152,6 @@ function task_populateDB(aArray)
let folderId = PlacesUtils.bookmarks.createFolder(qdata.parentFolder,
qdata.title,
qdata.index);
if (qdata.readOnly)
PlacesUtils.bookmarks.setFolderReadonly(folderId, true);
}
if (qdata.isLivemark) {
@ -246,7 +244,6 @@ function queryData(obj) {
this.dateAdded = obj.dateAdded ? obj.dateAdded : today;
this.keyword = obj.keyword ? obj.keyword : "";
this.visitCount = obj.visitCount ? obj.visitCount : 0;
this.readOnly = obj.readOnly ? obj.readOnly : false;
this.isSeparator = obj.hasOwnProperty("isSeparator") && obj.isSeparator;
// And now, the attribute for whether or not this object should appear in the

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

@ -313,7 +313,6 @@ let DataHelper = {
case "folder":
return {
isFolder: true,
readOnly: false,
parentFolder: dat.parent,
index: PlacesUtils.bookmarks.DEFAULT_INDEX,
title: dat.title,

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

@ -1,48 +0,0 @@
/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
/* vim:set ts=2 sw=2 sts=2 et: */
/* 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/. */
// The test data for our database, note that the ordering of the results that
// will be returned by the query (the isInQuery: true objects) is IMPORTANT.
// see compareArrayToResult in head_queries.js for more info.
var testData = [
// Normal folder
{ isInQuery: true, isFolder: true, title: "Folder 1",
parentFolder: PlacesUtils.toolbarFolderId },
// Read only folder
{ isInQuery: false, isFolder: true, title: "Folder 2 RO",
parentFolder: PlacesUtils.toolbarFolderId, readOnly: true }
];
function run_test()
{
run_next_test();
}
add_task(function test_excludeReadOnlyFolders()
{
yield task_populateDB(testData);
var query = PlacesUtils.history.getNewQuery();
query.setFolders([PlacesUtils.toolbarFolderId], 1);
// Options
var options = PlacesUtils.history.getNewQueryOptions();
options.excludeQueries = true;
options.excludeReadOnlyFolders = true;
// Results
var result = PlacesUtils.history.executeQuery(query, options);
var root = result.root;
root.containerOpen = true;
displayResultSet(root);
// The readonly folder should not be in our result set.
do_check_eq(1, root.childCount);
do_check_eq("Folder 1", root.getChild(0).title);
root.containerOpen = false;
});

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

@ -414,17 +414,6 @@ const queryOptionSwitches = [
}
]
},
// excludeReadOnlyFolders
{
property: "excludeReadOnlyFolders",
desc: "nsINavHistoryQueryOptions.excludeReadOnlyFolders",
matches: simplePropertyMatches,
runs: [
function (aQuery, aQueryOptions) {
aQueryOptions.excludeReadOnlyFolders = true;
}
]
},
// expandQueries
{
property: "expandQueries",

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

@ -8,7 +8,6 @@ skip-if = toolkit == 'android' || toolkit == 'gonk'
[test_abstime-annotation-uri.js]
[test_async.js]
[test_containersQueries_sorting.js]
[test_excludeReadOnlyFolders.js]
[test_history_queries_tags_liveUpdate.js]
[test_history_queries_titles_liveUpdate.js]
[test_onlyBookmarked.js]

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

@ -151,7 +151,7 @@ addMessageListener("cleanup", function () {
});
});
let AppFramesMock = {
let FramesMock = {
list: function () {
return Frames;
},
@ -159,4 +159,4 @@ let AppFramesMock = {
removeObserver: function () {}
};
require("devtools/server/actors/webapps").setAppFramesMock(AppFramesMock);
require("devtools/server/actors/webapps").setFramesMock(FramesMock);

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

@ -27,6 +27,8 @@ function ContentActor(connection, chromeGlobal)
this._chromeGlobal = chromeGlobal;
TabActor.call(this, connection, chromeGlobal);
this.traits.reconfigure = false;
this._sendForm = this._sendForm.bind(this);
this._chromeGlobal.addMessageListener("debug:form", this._sendForm);
}
ContentActor.prototype = Object.create(TabActor.prototype);
@ -41,19 +43,29 @@ Object.defineProperty(ContentActor.prototype, "docShell", {
configurable: true
});
Object.defineProperty(ContentActor.prototype, "title", {
get: function() {
return this.window.document.title;
},
enumerable: true,
configurable: true
});
ContentActor.prototype.exit = function() {
this._chromeGlobal.removeMessageListener("debug:form", this._sendForm);
this._sendForm = null;
TabActor.prototype.exit.call(this);
};
// Override grip just to rename this._tabActorPool to this._tabActorPool2
// Override form just to rename this._tabActorPool to this._tabActorPool2
// in order to prevent it to be cleaned on detach.
// We have to keep tab actors alive as we keep the ContentActor
// alive after detach and reuse it for multiple debug sessions.
ContentActor.prototype.grip = function () {
ContentActor.prototype.form = function () {
let response = {
'actor': this.actorID,
'title': this.title,
'url': this.url
"actor": this.actorID,
"title": this.title,
"url": this.url
};
// Walk over tab actors added by extensions and add them to a new ActorPool.
@ -68,3 +80,10 @@ ContentActor.prototype.grip = function () {
return response;
};
/**
* On navigation events, our URL and/or title may change, so we update our
* counterpart in the parent process that participates in the tab list.
*/
ContentActor.prototype._sendForm = function() {
this._chromeGlobal.sendAsyncMessage("debug:form", this.form());
};

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

@ -18,19 +18,19 @@ let { ActorPool } = require("devtools/server/actors/common");
let { DebuggerServer } = require("devtools/server/main");
let Services = require("Services");
let AppFramesMock = null;
let FramesMock = null;
exports.setAppFramesMock = function (mock) {
AppFramesMock = mock;
}
exports.setFramesMock = function (mock) {
FramesMock = mock;
};
DevToolsUtils.defineLazyGetter(this, "AppFrames", () => {
DevToolsUtils.defineLazyGetter(this, "Frames", () => {
// Offer a way for unit test to provide a mock
if (AppFramesMock) {
return AppFramesMock;
if (FramesMock) {
return FramesMock;
}
try {
return Cu.import("resource://gre/modules/AppFrames.jsm", {}).AppFrames;
return Cu.import("resource://gre/modules/Frames.jsm", {}).Frames;
} catch(e) {}
return null;
});
@ -860,8 +860,10 @@ WebappsActor.prototype = {
_appFrames: function () {
// Try to filter on b2g and mulet
if (AppFrames) {
return AppFrames.list();
if (Frames) {
return Frames.list().filter(frame => {
return frame.getAttribute('mozapp');
});
} else {
return [];
}
@ -955,8 +957,8 @@ WebappsActor.prototype = {
watchApps: function () {
// For now, app open/close events are only implement on b2g
if (AppFrames) {
AppFrames.addObserver(this);
if (Frames) {
Frames.addObserver(this);
}
Services.obs.addObserver(this, "webapps-installed", false);
Services.obs.addObserver(this, "webapps-uninstall", false);
@ -965,8 +967,8 @@ WebappsActor.prototype = {
},
unwatchApps: function () {
if (AppFrames) {
AppFrames.removeObserver(this);
if (Frames) {
Frames.removeObserver(this);
}
Services.obs.removeObserver(this, "webapps-installed", false);
Services.obs.removeObserver(this, "webapps-uninstall", false);
@ -974,8 +976,9 @@ WebappsActor.prototype = {
return {};
},
onAppFrameCreated: function (frame, isFirstAppFrame) {
if (!isFirstAppFrame) {
onFrameCreated: function (frame, isFirstAppFrame) {
let mozapp = frame.getAttribute('mozapp');
if (!mozapp || !isFirstAppFrame) {
return;
}
@ -995,8 +998,9 @@ WebappsActor.prototype = {
});
},
onAppFrameDestroyed: function (frame, isLastAppFrame) {
if (!isLastAppFrame) {
onFrameDestroyed: function (frame, isLastAppFrame) {
let mozapp = frame.getAttribute('mozapp');
if (!mozapp || !isLastAppFrame) {
return;
}

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

@ -267,12 +267,37 @@ BrowserTabList.prototype._getSelectedBrowser = function(aWindow) {
return aWindow.gBrowser ? aWindow.gBrowser.selectedBrowser : null;
};
/**
* Produces an iterable (in this case a generator) to enumerate all available
* browser tabs.
*/
BrowserTabList.prototype._getBrowsers = function*() {
// Iterate over all navigator:browser XUL windows.
for (let win of allAppShellDOMWindows(DebuggerServer.chromeWindowType)) {
// For each tab in this XUL window, ensure that we have an actor for
// it, reusing existing actors where possible. We actually iterate
// over 'browser' XUL elements, and BrowserTabActor uses
// browser.contentWindow as the debuggee global.
for (let browser of this._getChildren(win)) {
yield browser;
}
}
};
BrowserTabList.prototype._getChildren = function(aWindow) {
return aWindow.gBrowser.browsers;
};
BrowserTabList.prototype._isRemoteBrowser = function(browser) {
return browser.getAttribute("remote");
};
BrowserTabList.prototype.getList = function() {
let topXULWindow = Services.wm.getMostRecentWindow(DebuggerServer.chromeWindowType);
let selectedBrowser = null;
if (topXULWindow) {
selectedBrowser = this._getSelectedBrowser(topXULWindow);
}
// As a sanity check, make sure all the actors presently in our map get
// picked up when we iterate over all windows' tabs.
@ -286,40 +311,25 @@ BrowserTabList.prototype.getList = function() {
let actorPromises = [];
// Iterate over all navigator:browser XUL windows.
for (let win of allAppShellDOMWindows(DebuggerServer.chromeWindowType)) {
let selectedBrowser = this._getSelectedBrowser(win);
if (!selectedBrowser) {
continue;
for (let browser of this._getBrowsers()) {
// Do we have an existing actor for this browser? If not, create one.
let actor = this._actorByBrowser.get(browser);
if (actor) {
actorPromises.push(actor.update());
foundCount++;
} else if (this._isRemoteBrowser(browser)) {
actor = new RemoteBrowserTabActor(this._connection, browser);
this._actorByBrowser.set(browser, actor);
actorPromises.push(actor.connect());
} else {
actor = new BrowserTabActor(this._connection, browser,
browser.getTabBrowser());
this._actorByBrowser.set(browser, actor);
actorPromises.push(promise.resolve(actor));
}
// For each tab in this XUL window, ensure that we have an actor for
// it, reusing existing actors where possible. We actually iterate
// over 'browser' XUL elements, and BrowserTabActor uses
// browser.contentWindow as the debuggee global.
for (let browser of this._getChildren(win)) {
// Do we have an existing actor for this browser? If not, create one.
let actor = this._actorByBrowser.get(browser);
if (actor) {
actorPromises.push(promise.resolve(actor));
foundCount++;
} else if (browser.isRemoteBrowser) {
actor = new RemoteBrowserTabActor(this._connection, browser);
this._actorByBrowser.set(browser, actor);
let promise = actor.connect().then((form) => {
actor._form = form;
return actor;
});
actorPromises.push(promise);
} else {
actor = new BrowserTabActor(this._connection, browser, win.gBrowser);
this._actorByBrowser.set(browser, actor);
actorPromises.push(promise.resolve(actor));
}
// Set the 'selected' properties on all actors correctly.
actor.selected = (win === topXULWindow && browser === selectedBrowser);
}
// Set the 'selected' properties on all actors correctly.
actor.selected = browser === selectedBrowser;
}
if (this._testing && initialMapSize !== foundCount)
@ -738,9 +748,18 @@ TabActor.prototype = {
return null;
},
/**
* This is called by BrowserTabList.getList for existing tab actors prior to
* calling |form| below. It can be used to do any async work that may be
* needed to assemble the form.
*/
update: function() {
return promise.resolve(this);
},
form: function BTA_form() {
dbg_assert(!this.exited,
"grip() shouldn't be called on exited browser actor.");
"form() shouldn't be called on exited browser actor.");
dbg_assert(this.actorID,
"tab should have an actorID.");
@ -961,7 +980,7 @@ TabActor.prototype = {
return {
id: id,
url: window.location.href,
title: window.title,
title: window.document.title,
parentID: parentID
};
});
@ -1617,7 +1636,28 @@ function RemoteBrowserTabActor(aConnection, aBrowser)
RemoteBrowserTabActor.prototype = {
connect: function() {
return DebuggerServer.connectToChild(this._conn, this._browser);
let connect = DebuggerServer.connectToChild(this._conn, this._browser);
return connect.then(form => {
this._form = form;
return this;
});
},
get _mm() {
return this._browser.QueryInterface(Ci.nsIFrameLoaderOwner).frameLoader
.messageManager;
},
update: function() {
let deferred = promise.defer();
let onFormUpdate = msg => {
this._mm.removeMessageListener("debug:form", onFormUpdate);
this._form = msg.json;
deferred.resolve(this);
};
this._mm.addMessageListener("debug:form", onFormUpdate);
this._mm.sendAsyncMessage("debug:form");
return deferred.promise;
},
form: function() {
@ -1629,6 +1669,8 @@ RemoteBrowserTabActor.prototype = {
},
};
exports.RemoteBrowserTabActor = RemoteBrowserTabActor;
function BrowserAddonList(aConnection)
{
this._connection = aConnection;

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

@ -46,7 +46,7 @@ let chromeGlobal = this;
actorPool.addActor(actor);
conn.addActorPool(actorPool);
sendAsyncMessage("debug:actor", {actor: actor.grip(), childID: id});
sendAsyncMessage("debug:actor", {actor: actor.form(), childID: id});
});
addMessageListener("debug:connect", onConnect);