зеркало из https://github.com/mozilla/gecko-dev.git
Merge m-c to inbound a=merge
This commit is contained in:
Коммит
08f61089ac
|
@ -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);
|
||||
|
|
Загрузка…
Ссылка в новой задаче