Merge mozilla-central to tracemonkey.

This commit is contained in:
Robert Sayre 2010-10-26 14:16:43 -04:00
commit ab1de59e65
190 changed files with 5465 additions and 2217 deletions

View File

@ -2766,6 +2766,13 @@ nsAccessible::RemoveChild(nsAccessible* aChild)
if (aChild->mParent != this || aChild->mIndexInParent == -1)
return PR_FALSE;
if (aChild->mIndexInParent >= mChildren.Length() ||
mChildren[aChild->mIndexInParent] != aChild) {
NS_ERROR("Child is bound to parent but parent hasn't this child at its index!");
aChild->UnbindFromParent();
return PR_FALSE;
}
for (PRUint32 idx = aChild->mIndexInParent + 1; idx < mChildren.Length(); idx++)
mChildren[idx]->mIndexInParent--;

View File

@ -1538,7 +1538,7 @@ nsHyperTextAccessible::SetSelectionRange(PRInt32 aStartPos, PRInt32 aEndPos)
// XXX I'm not sure this can do synchronous scrolling. If the last param is
// set to true, this calling might flush the pending reflow. See bug 418470.
selCon->ScrollSelectionIntoView(nsISelectionController::SELECTION_NORMAL,
nsISelectionController::SELECTION_FOCUS_REGION, PR_FALSE);
nsISelectionController::SELECTION_FOCUS_REGION, 0);
}
return NS_OK;

View File

@ -330,6 +330,7 @@ pref("browser.search.log", false);
// Ordering of Search Engines in the Engine list.
pref("browser.search.order.1", "chrome://browser-region/locale/region.properties");
pref("browser.search.order.2", "chrome://browser-region/locale/region.properties");
pref("browser.search.order.3", "chrome://browser-region/locale/region.properties");
// search bar results always open in a new tab
pref("browser.search.openintab", false);

View File

@ -787,7 +787,6 @@ const gFormSubmitObserver = {
init: function()
{
this.panel = document.getElementById('invalid-form-popup');
this.panel.appendChild(document.createTextNode(""));
},
panelIsOpen: function()
@ -820,8 +819,7 @@ const gFormSubmitObserver = {
return;
}
// Limit the message to 256 characters.
this.panel.firstChild.nodeValue = element.validationMessage.substring(0, 256);
this.panel.firstChild.textContent = element.validationMessage;
element.focus();
@ -5092,54 +5090,28 @@ function asyncOpenWebPanel(event)
return true;
}
function handleLinkClick(event, href, linkNode)
{
function handleLinkClick(event, href, linkNode) {
if (event.button == 2) // right click
return false;
var where = whereToOpenLink(event);
if (where == "current")
return false;
var doc = event.target.ownerDocument;
switch (event.button) {
case 0: // if left button clicked
#ifdef XP_MACOSX
if (event.metaKey) { // Cmd
#else
if (event.ctrlKey) {
#endif
openNewTabWith(href, doc, null, event, false);
event.stopPropagation();
return true;
}
if (event.shiftKey && event.altKey) {
var feedService =
Cc["@mozilla.org/browser/feeds/result-service;1"].
getService(Ci.nsIFeedResultService);
feedService.forcePreviewPage = true;
loadURI(href, null, null, false);
return false;
}
if (event.shiftKey) {
openNewWindowWith(href, doc, null, false);
event.stopPropagation();
return true;
}
if (event.altKey) {
saveURL(href, linkNode ? gatherTextUnder(linkNode) : "", null, true,
true, doc.documentURIObject);
return true;
}
return false;
case 1: // if middle button clicked
var tab = gPrefService.getBoolPref("browser.tabs.opentabfor.middleclick");
if (tab)
openNewTabWith(href, doc, null, event, false);
else
openNewWindowWith(href, doc, null, false);
event.stopPropagation();
return true;
if (where == "save") {
saveURL(href, linkNode ? gatherTextUnder(linkNode) : "", null, true,
true, doc.documentURIObject);
return true;
}
return false;
urlSecurityCheck(href, doc.nodePrincipal);
openLinkIn(href, where, { fromContent: true,
referrerURI: doc.documentURIObject,
charset: doc.characterSet });
event.stopPropagation();
return true;
}
function middleMousePaste(event) {

View File

@ -167,7 +167,9 @@
<panel type="autocomplete-richlistbox" id="PopupAutoCompleteRichResult" noautofocus="true" hidden="true"/>
<!-- for invalid form error message -->
<panel id="invalid-form-popup" noautofocus="true" hidden="true" level="parent"/>
<panel id="invalid-form-popup" noautofocus="true" hidden="true" level="parent">
<description/>
</panel>
<panel id="editBookmarkPanel"
orient="vertical"

View File

@ -958,14 +958,17 @@ nsContextMenu.prototype = {
}
}
// in case we need to prompt the user for authentication
function callbacks() {}
callbacks.prototype = {
getInterface: function sLA_callbacks_getInterface(aIID) {
if (aIID.equals(Ci.nsIAuthPrompt) || aIID.equals(Ci.nsIAuthPrompt2)) {
var ww = Cc["@mozilla.org/embedcomp/window-watcher;1"].
getService(Ci.nsIPromptFactory);
return ww.getPrompt(doc.defaultView, aIID);
// If the channel demands authentication prompt, we must cancel it
// because the save-as-timer would expire and cancel the channel
// before we get credentials from user. Both authentication dialog
// and save as dialog would appear on the screen as we fall back to
// the old fashioned way after the timeout.
timer.cancel();
channel.cancel(NS_ERROR_SAVE_LINK_AS_TIMEOUT);
}
throw Cr.NS_ERROR_NO_INTERFACE;
}

View File

@ -70,6 +70,7 @@ function GroupItem(listOfEls, options) {
options = {};
this._inited = false;
this._uninited = false;
this._children = []; // an array of Items
this.defaultSize = new Point(TabItems.tabWidth * 1.5, TabItems.tabHeight * 1.5);
this.isAGroupItem = true;
@ -366,7 +367,7 @@ GroupItem.prototype = Utils.extend(new Item(), new Subscribable(), {
// Function: save
// Saves this groupItem to persistent storage.
save: function GroupItem_save() {
if (!this._inited) // too soon to save now
if (!this._inited || this._uninited) // too soon/late to save
return;
var data = this.getStorageData();
@ -374,6 +375,14 @@ GroupItem.prototype = Utils.extend(new Item(), new Subscribable(), {
Storage.saveGroupItem(gWindow, data);
},
// ----------
// Function: deleteData
// Deletes the groupItem in the persistent storage.
deleteData: function GroupItem_deleteData() {
this._uninited = true;
Storage.deleteGroupItem(gWindow, this.id);
},
// ----------
// Function: getTitle
// Returns the title of this groupItem as a string.
@ -561,7 +570,7 @@ GroupItem.prototype = Utils.extend(new Item(), new Subscribable(), {
}
});
Storage.deleteGroupItem(gWindow, this.id);
this.deleteData();
},
// ----------
@ -647,7 +656,7 @@ GroupItem.prototype = Utils.extend(new Item(), new Subscribable(), {
self.$undoContainer = null;
Items.unsquish();
Storage.deleteGroupItem(gWindow, self.id);
self.deleteData();
};
this.$undoContainer.click(function(e) {
@ -2072,7 +2081,7 @@ let GroupItems = {
child.close();
});
Storage.deleteGroupItem(gWindow, groupItem.id);
groupItem.deleteData();
}
});
}

View File

@ -202,11 +202,10 @@ function TabItem(tab, options) {
this._updateDebugBounds();
TabItems.register(this);
if (!this.reconnected) {
if (!this.reconnected)
GroupItems.newTab(this, options);
}
// tabs which were not reconnected at all or were not immediately added
// to a group get the same treatment.
if (!this.reconnected || (reconnected && !reconnected.addedToGroup) ) {
@ -804,7 +803,7 @@ let TabItems = {
let oldURL = tabItem.url;
tabItem.url = tabUrl;
if (!tabItem.reconnected && (oldURL == 'about:blank' || !oldURL))
if (!tabItem.reconnected)
this.reconnect(tabItem);
tabItem.save();
@ -1057,10 +1056,10 @@ let TabItems = {
item.reconnected = true;
found = {addedToGroup: tabData.groupID};
} else {
// if it's not a blank tab or it belongs to a group, it would mean
// the item is reconnected.
item.reconnected =
(item.tab.linkedBrowser.currentURI.spec != 'about:blank' || item.parent);
// We should never have any orphaned tabs. Therefore, item is not
// connected if it has no parent and GroupItems.newTab() would handle
// the group creation.
item.reconnected = (item.parent != null);
}
item.save();

View File

@ -16,9 +16,9 @@ function checkPopupHide()
function checkPopupMessage(doc)
{
is(gInvalidFormPopup.firstChild.nodeValue,
doc.getElementById('i').validationMessage.substring(0,256),
"The panel should show the 256 first characters of the validationMessage");
is(gInvalidFormPopup.firstChild.textContent,
doc.getElementById('i').validationMessage,
"The panel should show the message from validationMessage");
}
let gObserver = {
@ -132,50 +132,11 @@ function test3()
gBrowser.selectedTab.linkedBrowser.loadURI(uri);
}
/**
* In this test, we check that the validation message is correctly cut.
*/
function test4()
{
let uri = "data:text/html,<iframe name='t'></iframe><form target='t' action='data:text/html,'><input id='i'><input id='s' type='submit'></form>";
let tab = gBrowser.addTab();
gInvalidFormPopup.addEventListener("popupshown", function() {
gInvalidFormPopup.removeEventListener("popupshown", arguments.callee, false);
let doc = gBrowser.contentDocument;
is(doc.activeElement, doc.getElementById('i'),
"First invalid element should be focused");
checkPopupShow();
checkPopupMessage(doc);
// Clean-up and next test.
gBrowser.removeTab(gBrowser.selectedTab, {animate: false});
executeSoon(test5);
}, false);
tab.linkedBrowser.addEventListener("load", function(aEvent) {
tab.linkedBrowser.removeEventListener("load", arguments.callee, true);
let msg = "";
for (let i=0; i<50; ++i) {
msg += "abcde ";
}
// msg has 300 characters
gBrowser.contentDocument.getElementById('i').setCustomValidity(msg);
gBrowser.contentDocument.getElementById('s').click();
}, true);
gBrowser.selectedTab = tab;
gBrowser.selectedTab.linkedBrowser.loadURI(uri);
}
/**
* In this test, we check that, we can hide the popup by interacting with the
* invalid element.
*/
function test5()
function test4()
{
let uri = "data:text/html,<iframe name='t'></iframe><form target='t' action='data:text/html,'><input id='i' required><input id='s' type='submit'></form>";
let tab = gBrowser.addTab();
@ -197,7 +158,7 @@ function test5()
// Clean-up and next test.
gBrowser.removeTab(gBrowser.selectedTab, {animate: false});
executeSoon(test6);
executeSoon(test5);
});
}, false);
@ -215,7 +176,7 @@ function test5()
* In this test, we check that we can hide the popup by blurring the invalid
* element.
*/
function test6()
function test5()
{
let uri = "data:text/html,<iframe name='t'></iframe><form target='t' action='data:text/html,'><input id='i' required><input id='s' type='submit'></form>";
let tab = gBrowser.addTab();
@ -237,7 +198,7 @@ function test6()
// Clean-up and next test.
gBrowser.removeTab(gBrowser.selectedTab, {animate: false});
executeSoon(test7);
executeSoon(test6);
});
}, false);
@ -254,7 +215,7 @@ function test6()
/**
* In this test, we check that we can hide the popup by pressing TAB.
*/
function test7()
function test6()
{
let uri = "data:text/html,<iframe name='t'></iframe><form target='t' action='data:text/html,'><input id='i' required><input id='s' type='submit'></form>";
let tab = gBrowser.addTab();
@ -276,7 +237,7 @@ function test7()
// Clean-up and next test.
gBrowser.removeTab(gBrowser.selectedTab, {animate: false});
executeSoon(test8);
executeSoon(test7);
});
}, false);
@ -293,7 +254,7 @@ function test7()
/**
* In this test, we check that the popup will hide if we move to another tab.
*/
function test8()
function test7()
{
let uri = "data:text/html,<iframe name='t'></iframe><form target='t' action='data:text/html,'><input id='i' required><input id='s' type='submit'></form>";
let tab = gBrowser.addTab();
@ -317,7 +278,7 @@ function test8()
// Clean-up and next test.
gBrowser.removeTab(gBrowser.selectedTab, {animate: false});
gBrowser.removeTab(gBrowser.selectedTab, {animate: false});
executeSoon(test9);
executeSoon(test8);
});
}, false);
@ -336,7 +297,7 @@ function test8()
* invalid form is submitted in another tab than the current focused one
* (submitted in background).
*/
function test9()
function test8()
{
let uri = "data:text/html,<iframe name='t'></iframe><form target='t' action='data:text/html,'><input id='i' required><input id='s' type='submit'></form>";
let tab = gBrowser.addTab();
@ -355,7 +316,7 @@ function test9()
gBrowser.removeTab(tab, {animate: false});
// Next test
executeSoon(test10);
executeSoon(test9);
});
};
@ -376,7 +337,7 @@ function test9()
/**
* In this test, we check that the author defined error message is shown.
*/
function test10()
function test9()
{
let uri = "data:text/html,<iframe name='t'></iframe><form target='t' action='data:text/html,'><input x-moz-errormessage='foo' required id='i'><input id='s' type='submit'></form>";
let tab = gBrowser.addTab();
@ -390,7 +351,7 @@ function test10()
checkPopupShow();
is(gInvalidFormPopup.firstChild.nodeValue, "foo",
is(gInvalidFormPopup.firstChild.textContent, "foo",
"The panel should show the author defined error message");
// Clean-up and next test.

View File

@ -1,9 +1,36 @@
var tab;
function test() {
waitForExplicitFinish();
var tab = gBrowser.addTab();
executeSoon(function () {
gBrowser.removeTab(tab, {animate:true});
ok(!tab.parentNode, "tab successfully removed");
finish();
});
tab = gBrowser.addTab();
isnot(tab.getAttribute("fadein"), "true", "newly opened tab is yet to fade in");
// Try to remove the tab right before the opening animation's first frame
window.mozRequestAnimationFrame(checkAnimationState);
}
function checkAnimationState() {
if (tab.getAttribute("fadein") != "true") {
window.mozRequestAnimationFrame(checkAnimationState);
return;
}
info(window.getComputedStyle(tab).maxWidth);
gBrowser.removeTab(tab, { animate: true });
if (!tab.parentNode) {
ok(true, "tab removed synchronously since the opening animation hasn't moved yet");
finish();
return;
}
info("tab didn't close immediately, so the tab opening animation must have started moving");
info("waiting for the tab to close asynchronously");
tab.addEventListener("transitionend", function (event) {
if (event.propertyName == "max-width")
executeSoon(function () {
ok(!tab.parentNode, "tab removed asynchronously");
finish();
});
}, false);
}

View File

@ -56,6 +56,7 @@ _BROWSER_FILES = \
browser_tabview_bug595804.js \
browser_tabview_bug595930.js \
browser_tabview_bug595943.js \
browser_tabview_bug598600.js \
browser_tabview_dragdrop.js \
browser_tabview_exit_button.js \
browser_tabview_group.js \

View File

@ -0,0 +1,115 @@
/* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is tabview bug598600 test.
*
* The Initial Developer of the Original Code is
* Mozilla Foundation.
* Portions created by the Initial Developer are Copyright (C) 2010
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Raymond Lee <raymond@appcoast.com>
*
* Alternatively, the contents of this file may be used under the terms of
* either the GNU General Public License Version 2 or later (the "GPL"), or
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the MPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
let newWin;
let prefService;
function test() {
let ss = Cc["@mozilla.org/browser/sessionstore;1"].getService(Ci.nsISessionStore);
prefService =
Cc["@mozilla.org/preferences-service;1"].getService(Ci.nsIPrefService).
getBranch("browser.panorama.");
// make sure we don't trigger the 'first run' behavior
prefService.setBoolPref("experienced_first_run", true);
waitForExplicitFinish();
// open a new window and setup the window state.
newWin = openDialog(getBrowserURL(), "_blank", "chrome,all,dialog=no");
newWin.addEventListener("load", function(event) {
this.removeEventListener("load", arguments.callee, false);
let newState = {
windows: [{
tabs: [{
entries: [{ "url": "about:blank" }],
hidden: true,
attributes: {},
extData: {
"tabview-tab":
'{"bounds":{"left":20,"top":35,"width":280,"height":210},' +
'"userSize":null,"url":"about:blank","groupID":1,' +
'"imageData":null,"title":null}'
}
},{
entries: [{ url: "about:blank" }],
index: 1,
hidden: false,
attributes: {},
extData: {
"tabview-tab":
'{"bounds":{"left":375,"top":35,"width":280,"height":210},' +
'"userSize":null,"url":"about:blank","groupID":2,' +
'"imageData":null,"title":null}'
}
}],
selected:2,
_closedTabs: [],
extData: {
"tabview-groups": '{"nextID":3,"activeGroupId":2}',
"tabview-group":
'{"1":{"bounds":{"left":15,"top":10,"width":320,"height":375},' +
'"userSize":null,"locked":{},"title":"","id":1},' +
'"2":{"bounds":{"left":380,"top":5,"width":320,"height":375},' +
'"userSize":null,"locked":{},"title":"","id":2}}',
"tabview-ui": '{"pageBounds":{"left":0,"top":0,"width":875,"height":650}}'
}, sizemode:"normal"
}]
};
ss.setWindowState(newWin, JSON.stringify(newState), true);
// add a new tab.
newWin.gBrowser.addTab();
is(newWin.gBrowser.tabs.length, 3, "There are 3 browser tabs");
let onTabViewShow = function() {
newWin.removeEventListener("tabviewshown", onTabViewShow, false);
let contentWindow = newWin.document.getElementById("tab-view").contentWindow;
is(contentWindow.GroupItems.groupItems.length, 2, "Has two group items");
is(contentWindow.GroupItems.getOrphanedTabs().length, 0, "No orphan tabs");
// clean up and finish
prefService.setBoolPref("experienced_first_run", false);
newWin.close();
finish();
}
newWin.addEventListener("tabviewshown", onTabViewShow, false);
newWin.TabView.toggle();
}, false);
}

View File

@ -50,8 +50,12 @@ function getBrowserURL()
return "chrome://browser/content/browser.xul";
}
function getTopWin()
{
function getTopWin(skipPopups) {
if (skipPopups) {
return Components.classes["@mozilla.org/browser/browserglue;1"]
.getService(Components.interfaces.nsIBrowserGlue)
.getMostRecentBrowserWindow();
}
return Services.wm.getMostRecentWindow("navigator:browser");
}
@ -158,18 +162,33 @@ function whereToOpenLink( e, ignoreButton, ignoreAlt )
* relatedToCurrent (boolean)
*/
function openUILinkIn(url, where, aAllowThirdPartyFixup, aPostData, aReferrerURI) {
var params;
if (arguments.length == 3 && typeof arguments[2] == "object") {
params = aAllowThirdPartyFixup;
} else {
params = {
allowThirdPartyFixup: aAllowThirdPartyFixup,
postData: aPostData,
referrerURI: aReferrerURI
};
}
params.fromContent = false;
openLinkIn(url, where, params);
}
function openLinkIn(url, where, params) {
if (!where || !url)
return;
var aRelatedToCurrent;
if (arguments.length == 3 &&
typeof arguments[2] == "object") {
let params = arguments[2];
aAllowThirdPartyFixup = params.allowThirdPartyFixup;
aPostData = params.postData;
aReferrerURI = params.referrerURI;
aRelatedToCurrent = params.relatedToCurrent;
}
var aFromContent = params.fromContent;
var aAllowThirdPartyFixup = params.allowThirdPartyFixup;
var aPostData = params.postData;
var aCharset = params.charset;
var aReferrerURI = params.referrerURI;
var aRelatedToCurrent = params.relatedToCurrent;
if (where == "save") {
saveURL(url, null, null, true, null, aReferrerURI);
@ -179,6 +198,11 @@ function openUILinkIn(url, where, aAllowThirdPartyFixup, aPostData, aReferrerURI
const Ci = Components.interfaces;
var w = getTopWin();
if ((where == "tab" || where == "tabshifted") &&
w.document.documentElement.getAttribute("chromehidden")) {
w = getTopWin(true);
aRelatedToCurrent = false;
}
if (!w || where == "window") {
var sa = Cc["@mozilla.org/supports-array;1"].
@ -188,12 +212,19 @@ function openUILinkIn(url, where, aAllowThirdPartyFixup, aPostData, aReferrerURI
createInstance(Ci.nsISupportsString);
wuri.data = url;
let charset = null;
if (aCharset) {
charset = Cc["@mozilla.org/supports-string;1"]
.createInstance(Ci.nsISupportsString);
charset.data = "charset=" + aCharset;
}
var allowThirdPartyFixupSupports = Cc["@mozilla.org/supports-PRBool;1"].
createInstance(Ci.nsISupportsPRBool);
allowThirdPartyFixupSupports.data = aAllowThirdPartyFixup;
sa.AppendElement(wuri);
sa.AppendElement(null);
sa.AppendElement(charset);
sa.AppendElement(aReferrerURI);
sa.AppendElement(aPostData);
sa.AppendElement(allowThirdPartyFixupSupports);
@ -210,7 +241,9 @@ function openUILinkIn(url, where, aAllowThirdPartyFixup, aPostData, aReferrerURI
return;
}
var loadInBackground = getBoolPref("browser.tabs.loadBookmarksInBackground");
var loadInBackground = aFromContent ?
getBoolPref("browser.tabs.loadInBackground") :
getBoolPref("browser.tabs.loadBookmarksInBackground");
if (where == "current" && w.gBrowser.selectedTab.pinned) {
try {
@ -237,6 +270,7 @@ function openUILinkIn(url, where, aAllowThirdPartyFixup, aPostData, aReferrerURI
let browser = w.gBrowser;
browser.loadOneTab(url, {
referrerURI: aReferrerURI,
charset: aCharset,
postData: aPostData,
inBackground: loadInBackground,
allowThirdPartyFixup: aAllowThirdPartyFixup,
@ -449,7 +483,7 @@ function openTroubleshootingPage()
*/
function openFeedbackPage()
{
openUILinkIn("http://input.mozilla.com/sad", "tab");
openUILinkIn("http://input.mozilla.com/feedback", "tab");
}

View File

@ -762,6 +762,19 @@ var gCookiesWindow = {
this._view._filterSet.reverse();
}
// Adjust the Sort Indicator
var domainCol = document.getElementById("domainCol");
var nameCol = document.getElementById("nameCol");
var sortOrderString = ascending ? "ascending" : "descending";
if (aProperty == "rawHost") {
domainCol.setAttribute("sortDirection", sortOrderString);
nameCol.removeAttribute("sortDirection");
}
else {
nameCol.setAttribute("sortDirection", sortOrderString);
domainCol.removeAttribute("sortDirection");
}
this._view._invalidateCache(0);
this._view.selection.clearSelection();
this._view.selection.select(0);

View File

@ -45,8 +45,9 @@ include $(DEPTH)/config/autoconf.mk
include $(topsrcdir)/config/rules.mk
_BROWSER_TEST_FILES = browser_405664.js \
browser_415700.js \
browser_addEngine.js \
testEngine.xml \
testEngine.src \
browser_426329.js \
426329.xml \
browser_483086.js \

View File

@ -1,85 +0,0 @@
/* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is mozilla.org code.
*
* The Initial Developer of the Original Code is Mozilla Foundation.
* Portions created by the Initial Developer are Copyright (C) 2008
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Ryan Flint <rflint@dslr.net> (Original Author)
*
* Alternatively, the contents of this file may be used under the terms of
* either the GNU General Public License Version 2 or later (the "GPL"), or
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the MPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
var gSS = Services.search;
function observers(aSubject, aTopic, aData) {
switch (aData) {
case "engine-added":
test2();
break;
case "engine-current":
test3();
break;
case "engine-removed":
test4();
break;
}
}
function test() {
waitForExplicitFinish();
Services.obs.addObserver(observers, "browser-search-engine-modified", false);
gSS.addEngine("http://mochi.test:8888/browser/browser/components/search/test/testEngine.xml",
Ci.nsISearchEngine.DATA_XML, "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAIAAACQkWg2AAABGklEQVQoz2NgGB6AnZ1dUlJSXl4eSDIyMhLW4Ovr%2B%2Fr168uXL69Zs4YoG%2BLi4i5dusTExMTGxsbNzd3f37937976%2BnpmZmagbHR09J49e5YvX66kpATVEBYW9ubNm2nTphkbG7e2tp44cQLIuHfvXm5urpaWFlDKysqqu7v73LlzECMYIiIiHj58mJCQoKKicvXq1bS0NKBgW1vbjh074uPjgeqAXE1NzSdPnvDz84M0AEUvXLgAsW379u1z5swBen3jxo2zZ892cHB4%2BvQp0KlAfwI1cHJyghQFBwfv2rULokFXV%2FfixYu7d%2B8GGqGgoMDKyrpu3br9%2B%2FcDuXl5eVA%2FAEWBfoWHAdAYoNuAYQ0XAeoUERFhGDYAAPoUaT2dfWJuAAAAAElFTkSuQmCC",
false);
}
function test2() {
var engine = gSS.getEngineByName("Foo");
ok(engine, "Engine was added.");
var aEngine = gSS.getEngineByAlias("fooalias");
ok(!aEngine, "Alias was not parsed from engine description");
gSS.currentEngine = engine;
}
function test3() {
var engine = gSS.currentEngine;
is(engine.name, "Foo", "Current engine was changed successfully");
gSS.removeEngine(engine);
}
function test4() {
var engine = gSS.currentEngine;
ok(engine, "An engine is present.");
isnot(engine.name, "Foo", "Current engine reset after removal");
Services.obs.removeObserver(observers, "browser-search-engine-modified");
finish();
}

View File

@ -0,0 +1,168 @@
/* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is mozilla.org code.
*
* The Initial Developer of the Original Code is Mozilla Foundation.
* Portions created by the Initial Developer are Copyright (C) 2008
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Ryan Flint <rflint@dslr.net> (Original Author)
*
* Alternatively, the contents of this file may be used under the terms of
* either the GNU General Public License Version 2 or later (the "GPL"), or
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the MPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
var gSS = Services.search;
function observer(aSubject, aTopic, aData) {
if (!gCurrentTest) {
info("Observer called with no test active");
return;
}
let engine = aSubject.QueryInterface(Ci.nsISearchEngine);
info("Observer: " + aData + " for " + engine.name);
let method;
switch (aData) {
case "engine-added":
if (gCurrentTest.added)
method = "added"
break;
case "engine-current":
if (gCurrentTest.current)
method = "current";
break;
case "engine-removed":
if (gCurrentTest.removed)
method = "removed";
break;
}
if (method)
gCurrentTest[method](engine);
}
function checkEngine(checkObj, engineObj) {
info("Checking engine");
for (var prop in checkObj)
is(checkObj[prop], engineObj[prop], prop + " is correct");
}
var gTests = [
{
name: "opensearch install",
engine: {
name: "Foo",
alias: null,
description: "Foo Search",
searchForm: "http://mochi.test:8888/browser/browser/components/search/test/",
type: Ci.nsISearchEngine.TYPE_OPENSEARCH
},
run: function () {
gSS.addEngine("http://mochi.test:8888/browser/browser/components/search/test/testEngine.xml",
Ci.nsISearchEngine.DATA_XML, "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAIAAACQkWg2AAABGklEQVQoz2NgGB6AnZ1dUlJSXl4eSDIyMhLW4Ovr%2B%2Fr168uXL69Zs4YoG%2BLi4i5dusTExMTGxsbNzd3f37937976%2BnpmZmagbHR09J49e5YvX66kpATVEBYW9ubNm2nTphkbG7e2tp44cQLIuHfvXm5urpaWFlDKysqqu7v73LlzECMYIiIiHj58mJCQoKKicvXq1bS0NKBgW1vbjh074uPjgeqAXE1NzSdPnvDz84M0AEUvXLgAsW379u1z5swBen3jxo2zZ892cHB4%2BvQp0KlAfwI1cHJyghQFBwfv2rULokFXV%2FfixYu7d%2B8GGqGgoMDKyrpu3br9%2B%2FcDuXl5eVA%2FAEWBfoWHAdAYoNuAYQ0XAeoUERFhGDYAAPoUaT2dfWJuAAAAAElFTkSuQmCC",
false);
},
added: function (engine) {
ok(engine, "engine was added.");
checkEngine(this.engine, engine);
let engineFromSS = gSS.getEngineByName(this.engine.name);
is(engine, engineFromSS, "engine is obtainable via getEngineByName");
let aEngine = gSS.getEngineByAlias("fooalias");
ok(!aEngine, "Alias was not parsed from engine description");
gSS.currentEngine = engine;
},
current: function (engine) {
let currentEngine = gSS.currentEngine;
is(engine, currentEngine, "engine is current");
is(engine.name, this.engine.name, "current engine was changed successfully");
gSS.removeEngine(engine);
},
removed: function (engine) {
let currentEngine = gSS.currentEngine;
ok(currentEngine, "An engine is present.");
isnot(currentEngine.name, this.engine.name, "Current engine reset after removal");
nextTest();
}
},
{
name: "sherlock install",
engine: {
name: "Test Sherlock",
alias: null,
description: "Test Description",
searchForm: "http://example.com/searchform",
type: Ci.nsISearchEngine.TYPE_SHERLOCK
},
run: function () {
gSS.addEngine("http://mochi.test:8888/browser/browser/components/search/test/testEngine.src",
Ci.nsISearchEngine.DATA_TEXT, "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAIAAACQkWg2AAABGklEQVQoz2NgGB6AnZ1dUlJSXl4eSDIyMhLW4Ovr%2B%2Fr168uXL69Zs4YoG%2BLi4i5dusTExMTGxsbNzd3f37937976%2BnpmZmagbHR09J49e5YvX66kpATVEBYW9ubNm2nTphkbG7e2tp44cQLIuHfvXm5urpaWFlDKysqqu7v73LlzECMYIiIiHj58mJCQoKKicvXq1bS0NKBgW1vbjh074uPjgeqAXE1NzSdPnvDz84M0AEUvXLgAsW379u1z5swBen3jxo2zZ892cHB4%2BvQp0KlAfwI1cHJyghQFBwfv2rULokFXV%2FfixYu7d%2B8GGqGgoMDKyrpu3br9%2B%2FcDuXl5eVA%2FAEWBfoWHAdAYoNuAYQ0XAeoUERFhGDYAAPoUaT2dfWJuAAAAAElFTkSuQmCC",
false);
},
added: function (engine) {
ok(engine, "engine was added.");
checkEngine(this.engine, engine);
let engineFromSS = gSS.getEngineByName(this.engine.name);
is(engineFromSS, engine, "engine is obtainable via getEngineByName");
gSS.removeEngine(engine);
},
removed: function (engine) {
let currentEngine = gSS.currentEngine;
ok(currentEngine, "An engine is present.");
isnot(currentEngine.name, this.engine.name, "Current engine reset after removal");
nextTest();
}
}
];
var gCurrentTest = null;
function nextTest() {
if (gTests.length) {
gCurrentTest = gTests.shift();
info("Running " + gCurrentTest.name);
gCurrentTest.run();
} else
executeSoon(finish);
}
function test() {
waitForExplicitFinish();
Services.obs.addObserver(observer, "browser-search-engine-modified", false);
registerCleanupFunction(cleanup);
nextTest();
}
function cleanup() {
Services.obs.removeObserver(observer, "browser-search-engine-modified");
}

View File

@ -0,0 +1,11 @@
<search
name="Test Sherlock"
description="Test Description"
method="GET"
searchform="http://example.com/searchform"
action="http://example.com/action"
queryCharset="UTF-8"
>
<input name="userParam" user>
<input name="param" value="value">
</search>

View File

@ -1400,9 +1400,22 @@ SessionStoreService.prototype = {
if (!browser || !browser.currentURI)
// can happen when calling this function right after .addTab()
return tabData;
else if (browser.__SS_data && browser.__SS_data._tabStillLoading)
else if (browser.__SS_data && browser.__SS_data._tabStillLoading) {
// use the data to be restored when the tab hasn't been completely loaded
return browser.__SS_data;
tabData = browser.__SS_data;
if (aTab.pinned)
tabData.pinned = true;
else
delete tabData.pinned;
if (browser.userTypedValue) {
tabData.userTypedValue = browser.userTypedValue;
tabData.userTypedClear = browser.userTypedClear;
} else {
delete tabData.userTypedValue;
delete tabData.userTypedClear;
}
return tabData;
}
var history = null;
try {
@ -1443,10 +1456,15 @@ SessionStoreService.prototype = {
if (browser.userTypedValue) {
tabData.userTypedValue = browser.userTypedValue;
tabData.userTypedClear = browser.userTypedClear;
} else {
delete tabData.userTypedValue;
delete tabData.userTypedClear;
}
if (aTab.pinned)
tabData.pinned = true;
else
delete tabData.pinned;
tabData.hidden = aTab.hidden;
var disallow = [];

View File

@ -114,6 +114,7 @@ _BROWSER_TEST_FILES = \
browser_522545.js \
browser_524745.js \
browser_528776.js \
browser_579868.js \
browser_579879.js \
browser_580512.js \
browser_586147.js \

View File

@ -0,0 +1,71 @@
/* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is bug 579868 test.
*
* The Initial Developer of the Original Code is
* Sindre Dammann <sindrebugzilla@gmail.com>
* Portions created by the Initial Developer are Copyright (C) 2010
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
*
* Alternatively, the contents of this file may be used under the terms of
* either the GNU General Public License Version 2 or later (the "GPL"), or
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the MPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
function test() {
let tab1 = gBrowser.addTab("about:robots");
let tab2 = gBrowser.addTab("about:credits");
tab1.addEventListener("load", mainPart, true);
waitForExplicitFinish();
function mainPart() {
// Tell the session storer that the tab is pinned
// and that userTypedValue is "Hello World!"
let newTabState = '{"entries":[{"url":"about:robots"}],"pinned":true,"userTypedValue":"Hello World!"}';
let ss = Cc["@mozilla.org/browser/sessionstore;1"]
.getService(Ci.nsISessionStore);
ss.setTabState(tab1, newTabState);
// Undo pinning and userTypedValue
gBrowser.unpinTab(tab1);
tab1.linkedBrowser.userTypedValue = null;
is(tab1.linkedBrowser.__SS_data._tabStillLoading, true,
"_tabStillLoading should be true.");
// Close and restore tab
gBrowser.removeTab(tab1);
let savedState = JSON.parse(ss.getClosedTabData(window))[0].state;
isnot(savedState.pinned, true, "Pinned should not be true");
isnot(savedState.userTypedValue, "Hello World!",
"userTypedValue should not be Hello World!");
tab1 = ss.undoCloseTab(window, 0);
isnot(tab1.pinned, true, "Should not be pinned");
gBrowser.removeTab(tab1);
gBrowser.removeTab(tab2);
finish();
}
}

View File

@ -4,6 +4,7 @@ browser.search.defaultenginename=Google
# Search engine order (order displayed in the search bar dropdown)s
browser.search.order.1=Google
browser.search.order.2=Yahoo
browser.search.order.3=Bing
# This is the default set of web based feed handlers shown in the reader
# selection UI

View File

@ -0,0 +1,21 @@
<SearchPlugin xmlns="http://www.mozilla.org/2006/browser/search/">
<ShortName>Bing</ShortName>
<Description>Bing. Search by Microsoft.</Description>
<InputEncoding>UTF-8</InputEncoding>
<Image width="16" height="16" type="image/x-icon">data:image/x-icon;base64,AAABAAEAEBAAAAEAGABoAwAAFgAAACgAAAAQAAAAIAAAAAEAGAAAAAAAAAAAABMLAAATCwAAAAAAAAAAAAAVpv8Vpv8Vpv8Vpv8Vpv8Vpv8Vpv8Vpv8Vpv8Vpv8Vpv8Vpv8Vpv8Vpv8Vpv8Vpv8Vpv8Vpv8Vpv8Vpv8Vpv8Vpv8Vpv8Vpv8Vpv8Vpv8Vpv8Vpv8Vpv8Vpv8Vpv8Vpv8Vpv8Vpv8Vpv8Vpv8ysf97zf+24//F6f/F6f/F6f+K0/9QvP8Vpv8Vpv8Vpv8Vpv8Vpv8Vpv8krP+Z2P/////////w+f/F6f/F6f/i9P/////////T7v9Bt/8Vpv8Vpv8Vpv8Vpv/T7v/////w+f97zf8Vpv8Vpv8Vpv8Vpv9QvP/T7v/////w+f9Bt/8Vpv8Vpv97zf////////9QvP8Vpv8Vpv8Vpv8Vpv8Vpv8Vpv8krP/i9P/////i9P8Vpv8Vpv+24//////i9P8Vpv8Vpv8Vpv8Vpv8Vpv8Vpv8Vpv8Vpv+K0/////////8Vpv8Vpv/F6f////////8krP8Vpv8Vpv8Vpv8Vpv8Vpv8Vpv8Vpv+n3v/////w+f8Vpv8Vpv/F6f////////+n3v8krP8Vpv8Vpv8Vpv8Vpv8Vpv9tx/////////+Z2P8Vpv8Vpv/F6f/////////////i9P+K0/9QvP9QvP9tx//F6f////////+n3v8Vpv8Vpv8Vpv/F6f/////T7v+Z2P/i9P////////////////////+24/9QvP8Vpv8Vpv8Vpv8Vpv/F6f/////F6f8Vpv8Vpv8krP9QvP9QvP9Bt/8Vpv8Vpv8Vpv8Vpv8Vpv8Vpv8Vpv/F6f/////F6f8Vpv8Vpv8Vpv8Vpv8Vpv8Vpv8Vpv8Vpv8Vpv8Vpv8Vpv8Vpv8Vpv9Bt/9QvP9Bt/8Vpv8Vpv8Vpv8Vpv8Vpv8Vpv8Vpv8Vpv8Vpv8Vpv8Vpv8Vpv8Vpv8Vpv8Vpv8Vpv8Vpv8Vpv8Vpv8Vpv8Vpv8Vpv8Vpv8Vpv8Vpv8Vpv8Vpv8Vpv8Vpv8Vpv8Vpv8Vpv8Vpv8Vpv8Vpv8Vpv8Vpv8Vpv8Vpv8Vpv8Vpv8Vpv8Vpv8Vpv8AAHBsAABhdAAAbiAAAHJ0AABsaQAAdGkAACBDAABlbgAAUEEAAEVYAAAuQwAAOy4AAEU7AABBVAAAQ00AAC5W</Image>
<Url type="application/x-suggestions+json" template="http://api.bing.com/osjson.aspx">
<Param name="query" value="{searchTerms}"/>
<Param name="form" value="OSDJAS"/>
</Url>
<Url type="text/html" method="GET" template="http://www.bing.com/search">
<Param name="q" value="{searchTerms}"/>
<Param name="form" value="MOZSBR"/>
<MozParam name="pc" condition="pref" pref="ms-pc"/>
</Url>
<Url type="application/x-moz-keywordsearch" method="GET" template="http://www.bing.com/search">
<Param name="q" value="{searchTerms}"/>
<Param name="form" value="MOZLBR"/>
<MozParam name="pc" condition="pref" pref="ms-pc"/>
</Url>
<SearchForm>http://www.bing.com/search</SearchForm>
</SearchPlugin>

View File

@ -1,6 +1,5 @@
amazondotcom
answers
creativecommons
bing
eBay
google
wikipedia

View File

@ -1,13 +1,16 @@
af
ak
ar
be
bg
bn-BD
br
ca
cs
da
de
el
en-GB
en-US
en-ZA
eo
@ -43,6 +46,7 @@ ro
ru
sk
son
sq
sv-SE
tr
uk

View File

@ -28,7 +28,7 @@
-moz-border-bottom-colors: rgba(255,255,255,.5) rgba(83,42,6,.9);
-moz-border-right-colors: rgba(255,255,255,.5) rgba(83,42,6,.9);
box-shadow: 0 1px 0 rgba(255,255,255,.25) inset,
0 0 2px 1px rgba(255,255,255,.25) inset;
0 0 2px 1px rgba(255,255,255,.25) inset;
}
#appmenu-button:-moz-window-inactive {

View File

@ -5006,8 +5006,8 @@ BUILD_CTYPES=1
XPC_IDISPATCH_SUPPORT=
case "$target_os" in
darwin*|*wince*|*winmo*)
case "${target}" in
*android*|*darwin*|*wince*|*winmo*)
ACCESSIBILITY=
;;
*)
@ -8512,31 +8512,6 @@ if test "$MOZ_TREE_CAIRO"; then
AC_CHECK_HEADER(d3d10.h, MOZ_ENABLE_D3D10_LAYER=1)
fi
AC_TRY_COMPILE([#include <ddraw.h>], [int foo = DDLOCK_WAITNOTBUSY;], HAS_DDRAW=1, HAS_DDRAW=)
if test -z "$HAS_DDRAW"; then
AC_MSG_WARN([DirectDraw ddraw.h header not found or it's missing DDLOCK_WAITNOTBUSY, disabling DirectDraw surface. If you have an older SDK (such as the CE5 SDK), try copying in ddraw.lib and ddraw.h from the WM6 SDK.])
DDRAW_SURFACE_FEATURE=
else
DDRAW_SURFACE_FEATURE="#define CAIRO_HAS_DDRAW_SURFACE 1"
fi
if test -z "$OGLES_SDK_DIR"; then
OGLES_SURFACE_FEATURE=
else
AC_TRY_COMPILE([
#include <EGL/egl.h>
#include <EGL/eglext.h>
#include <GLES2/gl2.h>
#include <GLES2/gl2ext.h>
], [ EGLDisplay _cairo_ddraw_egl_dpy = EGL_NO_DISPLAY;], HAS_OGLES=1, HAS_OGLES=)
if test -z "$HAS_OGLES"; then
AC_MSG_WARN([OpenGL ES2 headers not found, disabling OpenGL acceleration surfaces.])
OGLES_SURFACE_FEATURE=
else
OGLES_SURFACE_FEATURE="#define CAIRO_DDRAW_USE_GL 1"
fi
fi
PDF_SURFACE_FEATURE="#define CAIRO_HAS_PDF_SURFACE 1"
fi
if test "$MOZ_WIDGET_TOOLKIT" = "os2"; then
@ -8581,8 +8556,6 @@ if test "$MOZ_TREE_CAIRO"; then
AC_SUBST(QUARTZ_IMAGE_SURFACE_FEATURE)
AC_SUBST(XCB_SURFACE_FEATURE)
AC_SUBST(WIN32_SURFACE_FEATURE)
AC_SUBST(DDRAW_SURFACE_FEATURE)
AC_SUBST(OGLES_SURFACE_FEATURE)
AC_SUBST(OS2_SURFACE_FEATURE)
AC_SUBST(BEOS_SURFACE_FEATURE)
AC_SUBST(DIRECTFB_SURFACE_FEATURE)

View File

@ -51,7 +51,7 @@ interface nsIDOMNode;
interface nsISelection;
interface nsISelectionDisplay;
[scriptable, uuid(bc5795ab-bcb5-448b-b3c7-a111bead7c26)]
[scriptable, uuid(ff11fa25-788f-444f-8f69-dcdf14348fb3)]
interface nsISelectionController : nsISelectionDisplay
{
const short SELECTION_NONE=0;
@ -94,20 +94,27 @@ interface nsISelectionController : nsISelectionDisplay
*/
nsISelection getSelection(in short type);
const short SCROLL_SYNCHRONOUS = 1<<1;
const short SCROLL_FIRST_ANCESTOR_ONLY = 1<<2;
/**
* ScrollSelectionIntoView scrolls a region of the selection,
* so that it is visible in the scrolled view.
*
* @param aType the selection to scroll into view. //SelectionType
* @param aRegion the region inside the selection to scroll into view. //SelectionRegion
* @param aIsSynchronous when true, scrolls the selection into view
* before returning. If false, posts a request which is processed
* @param aFlags the scroll flags. Valid bits include:
* SCROLL_SYNCHRONOUS: when set, scrolls the selection into view
* before returning. If not set, posts a request which is processed
* at some point after the method returns.
* SCROLL_FIRST_ANCESTOR_ONLY: if set, only the first ancestor will be scrolled
* into view.
*
* Note that if isSynchronous is true, then this might flush the pending
* reflow. It's dangerous for some objects. See bug 418470 comment 12.
*/
void scrollSelectionIntoView(in short type, in short region, in boolean isSynchronous);
void scrollSelectionIntoView(in short type, in short region, in short flags);
/**
* RepaintSelection repaints the selection specified by aType.
*

View File

@ -286,7 +286,7 @@ nsDOMFile::GetAsText(const nsAString &aCharset, nsAString &aResult)
rv = alias->GetPreferred(charsetGuess, charset);
NS_ENSURE_SUCCESS(rv, rv);
return ConvertStream(stream, charset.get(), aResult);
return DOMFileResult(ConvertStream(stream, charset.get(), aResult));
}
NS_IMETHODIMP
@ -338,8 +338,17 @@ nsDOMFile::GetAsDataURL(nsAString &aResult)
// out buffer should be at least 4/3rds the read buf, plus a terminator
char *base64 = PL_Base64Encode(readBuf, numEncode, nsnull);
AppendASCIItoUTF16(base64, aResult);
if (!base64) {
return DOMFileResult(NS_ERROR_OUT_OF_MEMORY);
}
nsDependentCString str(base64);
PRUint32 strLen = str.Length();
PRUint32 oldLength = aResult.Length();
AppendASCIItoUTF16(str, aResult);
PR_Free(base64);
if (aResult.Length() - oldLength != strLen) {
return DOMFileResult(NS_ERROR_OUT_OF_MEMORY);
}
if (leftOver) {
memmove(readBuf, readBuf + numEncode, leftOver);
@ -363,7 +372,11 @@ nsDOMFile::GetAsBinary(nsAString &aResult)
char readBuf[4096];
rv = stream->Read(readBuf, sizeof(readBuf), &numRead);
NS_ENSURE_SUCCESS(rv, DOMFileResult(rv));
PRUint32 oldLength = aResult.Length();
AppendASCIItoUTF16(Substring(readBuf, readBuf + numRead), aResult);
if (aResult.Length() - oldLength != numRead) {
return DOMFileResult(NS_ERROR_OUT_OF_MEMORY);
}
} while (numRead > 0);
return NS_OK;
@ -524,7 +537,11 @@ nsDOMFile::ConvertStream(nsIInputStream *aStream,
nsString result;
rv = unicharStream->ReadString(8192, result, &numChars);
while (NS_SUCCEEDED(rv) && numChars > 0) {
PRUint32 oldLength = aResult.Length();
aResult.Append(result);
if (aResult.Length() - oldLength != result.Length()) {
return NS_ERROR_OUT_OF_MEMORY;
}
rv = unicharStream->ReadString(8192, result, &numChars);
}

View File

@ -6,15 +6,13 @@
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"/>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
<script type="application/x-javascript">
var bodyOnLoad = false;
function checkOnLoad()
function onLoadFired()
{
ok(bodyOnLoad, "Body onload event should fire");
ok(true, "Body onload event should fire");
SimpleTest.finish();
}
SimpleTest.waitForExplicitFinish();
setTimeout(checkOnLoad, 500);
</script>
</head>
<body onload="bodyOnLoad = true;"/>
<body onload="onLoadFired();"/>
</html>

View File

@ -0,0 +1,31 @@
<!DOCTYPE html>
<html>
<head>
<script>
function boom()
{
var r = document.documentElement;
var i = document.getElementById("i");
document.removeChild(r);
document.appendChild(r);
w("dump('A\\n')");
document.removeChild(r);
w("dump('B\\n')");
document.appendChild(r);
function w(s)
{
var ns = document.createElement("script");
var nt = document.createTextNode(s);
ns.appendChild(nt);
i.appendChild(ns);
}
}
</script>
</head>
<body onload="boom();"><input id="i"></body>
</html>

View File

@ -20,3 +20,4 @@ load 580507-1.xhtml
load 590387.html
load 596785-1.html
load 596785-2.html
load 606430-1.html

View File

@ -64,6 +64,8 @@ public:
friend class nsDOMValidityState;
static const PRUint16 sContentSpecifiedMaxLengthMessage;
virtual ~nsIConstraintValidation();
PRBool IsValid() const { return mValidityBitField == 0; }

View File

@ -193,7 +193,7 @@ nsHTMLFieldSetElement::InsertChildAt(nsIContent* aChild, PRUint32 aIndex,
} else {
// If mFirstLegend is before aIndex, we do not change it.
// Otherwise, mFirstLegend is now aChild.
if (aIndex <= IndexOf(mFirstLegend)) {
if (PRInt32(aIndex) <= IndexOf(mFirstLegend)) {
mFirstLegend = aChild;
firstLegendHasChanged = true;
}

View File

@ -3972,6 +3972,9 @@ nsHTMLInputElement::GetValidationMessage(nsAString& aValidationMessage,
"FormValidationPatternMismatch",
message);
} else {
if (title.Length() > nsIConstraintValidation::sContentSpecifiedMaxLengthMessage) {
title.Truncate(nsIConstraintValidation::sContentSpecifiedMaxLengthMessage);
}
const PRUnichar* params[] = { title.get() };
rv = nsContentUtils::FormatLocalizedString(nsContentUtils::eDOM_PROPERTIES,
"FormValidationPatternMismatchWithTitle",

View File

@ -45,6 +45,8 @@
#include "nsHTMLFormElement.h"
const PRUint16 nsIConstraintValidation::sContentSpecifiedMaxLengthMessage = 256;
nsIConstraintValidation::nsIConstraintValidation()
: mValidityBitField(0)
, mValidity(nsnull)
@ -87,8 +89,14 @@ nsIConstraintValidation::GetValidationMessage(nsAString& aValidationMessage)
if (!authorMessage.IsEmpty()) {
aValidationMessage.Assign(authorMessage);
if (aValidationMessage.Length() > sContentSpecifiedMaxLengthMessage) {
aValidationMessage.Truncate(sContentSpecifiedMaxLengthMessage);
}
} else if (GetValidityState(VALIDITY_STATE_CUSTOM_ERROR)) {
aValidationMessage.Assign(mCustomValidity);
if (aValidationMessage.Length() > sContentSpecifiedMaxLengthMessage) {
aValidationMessage.Truncate(sContentSpecifiedMaxLengthMessage);
}
} else if (GetValidityState(VALIDITY_STATE_TOO_LONG)) {
GetValidationMessage(aValidationMessage, VALIDITY_STATE_TOO_LONG);
} else if (GetValidityState(VALIDITY_STATE_VALUE_MISSING)) {

View File

@ -78,6 +78,30 @@ struct SelectionState {
PRInt32 mEnd;
};
class RestoreSelectionState : public nsRunnable {
public:
RestoreSelectionState(nsTextControlFrame *aFrame, PRInt32 aStart, PRInt32 aEnd)
: mFrame(aFrame),
mWeakFrame(aFrame),
mStart(aStart),
mEnd(aEnd)
{
}
NS_IMETHOD Run() {
if (mWeakFrame.IsAlive()) {
mFrame->SetSelectionRange(mStart, mEnd);
}
return NS_OK;
}
private:
nsTextControlFrame* mFrame;
nsWeakFrame mWeakFrame;
PRInt32 mStart;
PRInt32 mEnd;
};
/*static*/
PRBool
nsITextControlElement::GetWrapPropertyEnum(nsIContent* aContent,
@ -160,7 +184,7 @@ public:
NS_IMETHOD SetSelectionFlags(PRInt16 aInEnable);
NS_IMETHOD GetSelectionFlags(PRInt16 *aOutEnable);
NS_IMETHOD GetSelection(PRInt16 type, nsISelection **_retval);
NS_IMETHOD ScrollSelectionIntoView(PRInt16 aType, PRInt16 aRegion, PRBool aIsSynchronous);
NS_IMETHOD ScrollSelectionIntoView(PRInt16 aType, PRInt16 aRegion, PRInt16 aFlags);
NS_IMETHOD RepaintSelection(PRInt16 type);
NS_IMETHOD RepaintSelection(nsPresContext* aPresContext, SelectionType aSelectionType);
NS_IMETHOD SetCaretEnabled(PRBool enabled);
@ -279,12 +303,12 @@ nsTextInputSelectionImpl::GetSelection(PRInt16 type, nsISelection **_retval)
}
NS_IMETHODIMP
nsTextInputSelectionImpl::ScrollSelectionIntoView(PRInt16 aType, PRInt16 aRegion, PRBool aIsSynchronous)
nsTextInputSelectionImpl::ScrollSelectionIntoView(PRInt16 aType, PRInt16 aRegion, PRInt16 aFlags)
{
if (!mFrameSelection)
return NS_ERROR_FAILURE;
return mFrameSelection->ScrollSelectionIntoView(aType, aRegion, aIsSynchronous);
return mFrameSelection->ScrollSelectionIntoView(aType, aRegion, aFlags);
}
NS_IMETHODIMP
@ -461,7 +485,8 @@ nsTextInputSelectionImpl::PageMove(PRBool aForward, PRBool aExtend)
}
// After ScrollSelectionIntoView(), the pending notifications might be
// flushed and PresShell/PresContext/Frames may be dead. See bug 418470.
return ScrollSelectionIntoView(nsISelectionController::SELECTION_NORMAL, nsISelectionController::SELECTION_FOCUS_REGION, PR_TRUE);
return ScrollSelectionIntoView(nsISelectionController::SELECTION_NORMAL, nsISelectionController::SELECTION_FOCUS_REGION,
nsISelectionController::SCROLL_SYNCHRONOUS);
}
NS_IMETHODIMP
@ -1328,7 +1353,7 @@ nsTextEditorState::PrepareEditor(const nsAString *aValue)
// Restore our selection after being bound to a new frame
if (mSelState) {
mBoundFrame->SetSelectionRange(mSelState->mStart, mSelState->mEnd);
nsContentUtils::AddScriptRunner(new RestoreSelectionState(mBoundFrame, mSelState->mStart, mSelState->mEnd));
mSelState = nsnull;
}

View File

@ -235,6 +235,7 @@ _TEST_FILES = \
test_bug596350.html \
test_bug600155.html \
test_bug556007.html \
test_bug606817.html \
$(NULL)
libs:: $(_TEST_FILES)

View File

@ -0,0 +1,65 @@
<!DOCTYPE HTML>
<html>
<!--
https://bugzilla.mozilla.org/show_bug.cgi?id=606817
-->
<head>
<title>Test for Bug 606817</title>
<script type="application/javascript" src="/MochiKit/packed.js"></script>
<script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
</head>
<body>
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=606817">Mozilla Bug 606817</a>
<p id="display"></p>
<pre id="test">
<script type="application/javascript">
/** Test for Bug 606817 **/
var messageMaxLength = 256;
function checkMessage(aInput, aMsg, aWithTerminalPeriod)
{
ok(aInput.validationMessage != aMsg,
"Original content-defined message should have been truncate");
is(aInput.validationMessage.length - aInput.validationMessage.indexOf("_42_"),
aWithTerminalPeriod ? messageMaxLength+1 : messageMaxLength,
"validation message should be 256 characters length");
}
var input = document.createElement("input");
var msg = "";
for (var i=0; i<75; ++i) {
msg += "_42_";
}
// msg is now 300 chars long
// Testing with setCustomValidity().
input.setCustomValidity(msg);
checkMessage(input, msg, false);
// The input is still invalid but x-moz-errormessage will be used as the message.
input.setAttribute("x-moz-errormessage", msg);
checkMessage(input, msg, false);
// Cleaning.
input.setCustomValidity("");
input.removeAttribute("x-moz-errormessage");
// Testing with pattern and titl.
input.pattern = "[0-9]*";
input.value = "foo";
input.title = msg;
checkMessage(input, msg, true);
// Cleaning.
input.removeAttribute("pattern");
input.removeAttribute("title");
input.value = "";
</script>
</pre>
</body>
</html>

View File

@ -65,9 +65,10 @@ function startTest(test, token) {
if (v.parentNode) {
v.parentNode.removeChild(v);
}
dump("SEEK-TEST: Finished " + name + "\n");
manager.finished(v.token);
}}(v, manager);
dump("Seek test: " + test.number + "\n");
dump("SEEK-TEST: Started " + name + "\n");
window['test_seek' + test.number](v, test.duration/2, localIs, localOk, localFinish);
}

View File

@ -404,6 +404,8 @@ nsXULCommandDispatcher::UpdateCommands(const nsAString& aEventName)
if (NS_FAILED(rv)) return rv;
}
nsCOMArray<nsIContent> updaters;
for (Updater* updater = mUpdaters; updater != nsnull; updater = updater->mNext) {
// Skip any nodes that don't match our 'events' or 'targets'
// filters.
@ -418,6 +420,12 @@ nsXULCommandDispatcher::UpdateCommands(const nsAString& aEventName)
if (! content)
return NS_ERROR_UNEXPECTED;
updaters.AppendObject(content);
}
for (PRUint32 u = 0; u < updaters.Count(); u++) {
nsIContent* content = updaters[u];
nsCOMPtr<nsIDocument> document = content->GetDocument();
NS_ASSERTION(document != nsnull, "element has no document");
@ -430,7 +438,7 @@ nsXULCommandDispatcher::UpdateCommands(const nsAString& aEventName)
CopyUTF16toUTF8(aEventName, aeventnameC);
PR_LOG(gLog, PR_LOG_NOTICE,
("xulcmd[%p] update %p event=%s",
this, updater->mElement.get(),
this, content,
aeventnameC.get()));
}
#endif

View File

@ -57,6 +57,8 @@ _TEST_FILES = \
$(NULL)
_CHROME_FILES = \
test_bug583948.xul \
window_bug583948.xul \
test_bug497875.xul \
bug497875-iframe.xul \
$(NULL)

View File

@ -0,0 +1,41 @@
<?xml version="1.0"?>
<?xml-stylesheet href="chrome://global/skin" type="text/css"?>
<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css"
type="text/css"?>
<window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
<script type="application/javascript" src="chrome://mochikit/content/MochiKit/packed.js"></script>
<script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js" />
<body xmlns="http://www.w3.org/1999/xhtml">
<div id="content" style="display: none"/>
</body>
<script>
SimpleTest.waitForExplicitFinish();
var attempts = 0;
function update() {
setTimeout(function() {
if (otherWindow.location)
otherWindow.location.reload()
}, 1);
otherWindow.document.commandDispatcher.updateCommands('');
// without the crash fix, this usually crashes after 2 to 4 reloads
if (++attempts == 6) {
ok(true, "didn't crash after 6 attempts");
otherWindow.close();
SimpleTest.finish();
}
else {
setTimeout(update, 100);
}
}
var otherWindow = window.open("window_bug583948.xul", "_new", "chrome");
setTimeout(update, 100);
</script>
</window>

View File

@ -0,0 +1,8 @@
<?xml-stylesheet href="chrome://browser/skin/" type="text/css"?>
<window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
<command oncommandupdate="document.removeChild(document.documentElement)" commandupdater="true"/>
<box command="c"/>
<iframe/>
</window>

View File

@ -1139,13 +1139,8 @@ nsXULTemplateBuilder::AttributeChanged(nsIDocument* aDocument,
// Check for a change to the 'datasources' attribute. If so, setup
// mDB by parsing the new value and rebuild.
else if (aAttribute == nsGkAtoms::datasources) {
Uninit(PR_FALSE); // Reset results
PRBool shouldDelay;
LoadDataSources(aDocument, &shouldDelay);
if (!shouldDelay)
nsContentUtils::AddScriptRunner(
NS_NewRunnableMethod(this, &nsXULTemplateBuilder::RunnableRebuild));
nsContentUtils::AddScriptRunner(
NS_NewRunnableMethod(this, &nsXULTemplateBuilder::RunnableLoadAndRebuild));
}
}
}
@ -1163,8 +1158,9 @@ nsXULTemplateBuilder::ContentRemoved(nsIDocument* aDocument,
if (mQueryProcessor)
mQueryProcessor->Done();
// use false since content is going away anyway
Uninit(PR_FALSE);
// Pass false to Uninit since content is going away anyway
nsContentUtils::AddScriptRunner(
NS_NewRunnableMethod(this, &nsXULTemplateBuilder::UninitFalse));
aDocument->RemoveObserver(this);
@ -1201,7 +1197,8 @@ nsXULTemplateBuilder::NodeWillBeDestroyed(const nsINode* aNode)
mCompDB = nsnull;
mRoot = nsnull;
Uninit(PR_TRUE);
nsContentUtils::AddScriptRunner(
NS_NewRunnableMethod(this, &nsXULTemplateBuilder::UninitTrue));
}

View File

@ -152,6 +152,20 @@ public:
RebuildAll() = 0; // must be implemented by subclasses
void RunnableRebuild() { Rebuild(); }
void RunnableLoadAndRebuild() {
Uninit(PR_FALSE); // Reset results
nsCOMPtr<nsIDocument> doc = mRoot ? mRoot->GetDocument() : nsnull;
if (doc) {
PRBool shouldDelay;
LoadDataSources(doc, &shouldDelay);
if (!shouldDelay) {
Rebuild();
}
}
}
void UninitFalse() { Uninit(PR_FALSE); }
void UninitTrue() { Uninit(PR_TRUE); }
/**
* Find the <template> tag that applies for this builder

View File

@ -27,8 +27,7 @@ function checkConsole(expectedError)
{
var out = {};
consoleService.getMessageArray(out, {});
var messages = out.value || [];
is(messages[0].message, expectedError, "logged message " + expectedError);
is(out.value[0].message, expectedError, "logged message " + expectedError);
}
// each test consists of a pre function executed before the template build, an

View File

@ -69,6 +69,8 @@
#include "nsComputedDOMStyle.h"
#include "nsIViewObserver.h"
#include "nsIPresShell.h"
#include "nsStyleAnimation.h"
#include "nsCSSProps.h"
#if defined(MOZ_X11) && defined(MOZ_WIDGET_GTK2)
#include <gdk/gdk.h>
@ -1495,6 +1497,74 @@ nsDOMWindowUtils::GetLayerManagerType(nsAString& aType)
return NS_OK;
}
static PRBool
ComputeAnimationValue(nsCSSProperty aProperty, nsIContent* aContent,
const nsAString& aInput,
nsStyleAnimation::Value& aOutput)
{
if (!nsStyleAnimation::ComputeValue(aProperty, aContent, aInput,
PR_FALSE, aOutput)) {
return PR_FALSE;
}
// This matches TransExtractComputedValue in nsTransitionManager.cpp.
if (aProperty == eCSSProperty_visibility) {
NS_ABORT_IF_FALSE(aOutput.GetUnit() == nsStyleAnimation::eUnit_Enumerated,
"unexpected unit");
aOutput.SetIntValue(aOutput.GetIntValue(),
nsStyleAnimation::eUnit_Visibility);
}
return PR_TRUE;
}
NS_IMETHODIMP
nsDOMWindowUtils::ComputeAnimationDistance(nsIDOMElement* aElement,
const nsAString& aProperty,
const nsAString& aValue1,
const nsAString& aValue2,
double* aResult)
{
if (!IsUniversalXPConnectCapable()) {
return NS_ERROR_DOM_SECURITY_ERR;
}
nsresult rv;
nsCOMPtr<nsIContent> content = do_QueryInterface(aElement, &rv);
NS_ENSURE_SUCCESS(rv, rv);
// Convert direction-dependent properties as appropriate, e.g.,
// border-left to border-left-value.
nsCSSProperty property = nsCSSProps::LookupProperty(aProperty);
if (property != eCSSProperty_UNKNOWN && nsCSSProps::IsShorthand(property)) {
nsCSSProperty subprop0 = *nsCSSProps::SubpropertyEntryFor(property);
if (nsCSSProps::PropHasFlags(subprop0, CSS_PROPERTY_REPORT_OTHER_NAME) &&
nsCSSProps::OtherNameFor(subprop0) == property) {
property = subprop0;
} else {
property = eCSSProperty_UNKNOWN;
}
}
NS_ABORT_IF_FALSE(property == eCSSProperty_UNKNOWN ||
!nsCSSProps::IsShorthand(property),
"should not have shorthand");
nsStyleAnimation::Value v1, v2;
if (property == eCSSProperty_UNKNOWN ||
!ComputeAnimationValue(property, content, aValue1, v1) ||
!ComputeAnimationValue(property, content, aValue2, v2)) {
return NS_ERROR_ILLEGAL_VALUE;
}
if (!nsStyleAnimation::ComputeDistance(property, v1, v2, *aResult)) {
return NS_ERROR_FAILURE;
}
return NS_OK;
}
nsresult
nsDOMWindowUtils::RenderDocument(const nsRect& aRect,
PRUint32 aFlags,

View File

@ -1238,6 +1238,9 @@ nsJSContext::JSOptionChangedCallback(const char *pref, void *data)
}
}
if (!chromeWindow)
newDefaultJSOptions |= JSOPTION_ROPES;
if (useTraceJIT)
newDefaultJSOptions |= JSOPTION_JIT;
else

View File

@ -65,7 +65,7 @@ interface nsITransferable;
interface nsIQueryContentEventResult;
interface nsIDOMWindow;
[scriptable, uuid(33cbae2d-2361-4f3d-b589-cc55af07a434)]
[scriptable, uuid(def3fbe8-961b-4c8b-8cac-eb6a4e604bb5)]
interface nsIDOMWindowUtils : nsISupports {
/**
@ -808,4 +808,16 @@ interface nsIDOMWindowUtils : nsISupports {
in PRUint32 aFlags,
in nscolor aBackgroundColor,
in gfxContext aThebesContext);
/**
* Method for testing nsStyleAnimation::ComputeDistance.
*
* Returns the distance between the two values as reported by
* nsStyleAnimation::ComputeDistance for the given element and
* property.
*/
double computeAnimationDistance(in nsIDOMElement element,
in AString property,
in AString value1,
in AString value2);
};

View File

@ -74,6 +74,7 @@ FormValidationSelectMissing=Please select an item in the list.
FormValidationInvalidEmail=Please enter an email address.
FormValidationInvalidURL=Please enter a URL.
FormValidationPatternMismatch=Please match the requested format.
# LOCALIZATION NOTE (FormValidationPatternMismatchWithTitle): %S is the (possibly truncated) title attribute value.
FormValidationPatternMismatchWithTitle=Please match the requested format: %S.
UseOfDocumentWidthWarning=Non-standard document.width was used. Use standard document.body.clientWidth instead.
UseOfDocumentHeightWarning=Non-standard document.height was used. Use standard document.body.clientHeight instead.

View File

@ -76,6 +76,10 @@ static const PRUint32 DEFAULT_OFFLINE_APP_QUOTA = 200 * 1024;
// ... but warn if it goes over this amount
static const PRUint32 DEFAULT_OFFLINE_WARN_QUOTA = 50 * 1024;
// Intervals to flush the temporary table after in seconds
#define NS_DOMSTORAGE_MAXIMUM_TEMPTABLE_INACTIVITY_TIME (5)
#define NS_DOMSTORAGE_MAXIMUM_TEMPTABLE_AGE (30)
static const char kPermissionType[] = "cookie";
static const char kStorageEnabled[] = "dom.storage.enabled";
static const char kDefaultQuota[] = "dom.storage.default_quota";
@ -414,6 +418,10 @@ nsDOMStorageManager::Observe(nsISupports *aSubject,
return nsDOMStorage::gStorageDB->DropSessionOnlyStoragesForHost(host);
#endif
}
} else if (!strcmp(aTopic, "timer-callback")) {
nsCOMPtr<nsIObserverService> obsserv = mozilla::services::GetObserverService();
if (obsserv)
obsserv->NotifyObservers(nsnull, NS_DOMSTORAGE_FLUSH_TIMER_OBSERVER, nsnull);
}
return NS_OK;
@ -533,6 +541,8 @@ NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsDOMStorage)
NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIDOMStorageObsolete)
NS_INTERFACE_MAP_ENTRY(nsIDOMStorageObsolete)
NS_INTERFACE_MAP_ENTRY(nsPIDOMStorage)
NS_INTERFACE_MAP_ENTRY(nsIObserver)
NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference)
NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO(StorageObsolete)
NS_INTERFACE_MAP_END
@ -563,6 +573,7 @@ nsDOMStorage::nsDOMStorage()
, mItemsCached(PR_FALSE)
, mEventBroadcaster(nsnull)
, mCanUseChromePersist(false)
, mLoadedTemporaryTable(false)
{
mSecurityChecker = this;
mItems.Init(8);
@ -581,6 +592,9 @@ nsDOMStorage::nsDOMStorage(nsDOMStorage& aThat)
#endif
, mEventBroadcaster(nsnull)
, mCanUseChromePersist(aThat.mCanUseChromePersist)
, mLoadedTemporaryTable(aThat.mLoadedTemporaryTable)
, mLastTemporaryTableAccessTime(aThat.mLastTemporaryTableAccessTime)
, mTemporaryTableAge(aThat.mTemporaryTableAge)
{
mSecurityChecker = this;
mItems.Init(8);
@ -689,6 +703,8 @@ nsDOMStorage::InitAsLocalStorage(nsIPrincipal *aPrincipal, const nsSubstring &aD
mCanUseChromePersist = URICanUseChromePersist(URI);
}
RegisterObservers();
return NS_OK;
}
@ -714,6 +730,9 @@ nsDOMStorage::InitAsGlobalStorage(const nsACString &aDomainDemanded)
mStorageType = GlobalStorage;
mEventBroadcaster = this;
RegisterObservers();
return NS_OK;
}
@ -1041,31 +1060,32 @@ nsDOMStorage::SetItem(const nsAString& aKey, const nsAString& aData)
if (aKey.IsEmpty())
return NS_OK;
nsresult rv;
nsString oldValue;
SetDOMStringToNull(oldValue);
nsresult rv;
nsRefPtr<nsDOMStorageItem> newitem = nsnull;
// First store the value to the database, we need to do this before we update
// the mItems cache. SetDBValue is using the old cached value to decide
// on quota checking.
bool isCallerSecure = IsCallerSecure();
if (UseDB()) {
rv = SetDBValue(aKey, aData, isCallerSecure);
NS_ENSURE_SUCCESS(rv, rv);
}
nsSessionStorageEntry *entry = mItems.GetEntry(aKey);
if (entry) {
if (entry->mItem->IsSecure() && !IsCallerSecure()) {
if (entry->mItem->IsSecure() && !isCallerSecure) {
return NS_ERROR_DOM_SECURITY_ERR;
}
oldValue = entry->mItem->GetValueInternal();
entry->mItem->SetValueInternal(aData);
}
else {
newitem = new nsDOMStorageItem(this, aKey, aData, IsCallerSecure());
nsRefPtr<nsDOMStorageItem> newitem =
new nsDOMStorageItem(this, aKey, aData, isCallerSecure);
if (!newitem)
return NS_ERROR_OUT_OF_MEMORY;
}
if (UseDB()) {
rv = SetDBValue(aKey, aData, IsCallerSecure());
NS_ENSURE_SUCCESS(rv, rv);
}
if (newitem) {
entry = mItems.PutEntry(aKey);
NS_ENSURE_TRUE(entry, NS_ERROR_OUT_OF_MEMORY);
entry->mItem = newitem;
@ -1110,7 +1130,7 @@ NS_IMETHODIMP nsDOMStorage::RemoveItem(const nsAString& aKey)
aKey.Length() + value.Length());
NS_ENSURE_SUCCESS(rv, rv);
mItemsCached = PR_FALSE;
// Before bug 536544 got fixed we were dropping mItemsCached flag here
#endif
}
else if (entry) {
@ -1232,6 +1252,23 @@ nsDOMStorage::CacheKeysFromDB()
return NS_OK;
}
nsresult
nsDOMStorage::GetCachedValue(const nsAString& aKey, nsAString& aValue,
PRBool* aSecure)
{
aValue.Truncate();
*aSecure = PR_FALSE;
nsSessionStorageEntry *entry = mItems.GetEntry(aKey);
if (!entry)
return NS_ERROR_NOT_AVAILABLE;
aValue = entry->mItem->GetValueInternal();
*aSecure = entry->mItem->IsSecure();
return NS_OK;
}
nsresult
nsDOMStorage::GetDBValue(const nsAString& aKey, nsAString& aValue,
PRBool* aSecure)
@ -1289,7 +1326,7 @@ nsDOMStorage::SetDBValue(const nsAString& aKey,
&usage);
NS_ENSURE_SUCCESS(rv, rv);
mItemsCached = PR_FALSE;
// Before bug 536544 got fixed we were dropping mItemsCached flag here
if (warnQuota >= 0 && usage > warnQuota) {
// try to include the window that exceeded the warn quota
@ -1468,6 +1505,84 @@ nsDOMStorage::BroadcastChangeNotification(const nsSubstring &aKey,
NS_ConvertUTF8toUTF16(mDomain).get());
}
nsresult
nsDOMStorage::MaybeCommitTemporaryTable(bool force)
{
#ifdef MOZ_STORAGE
if (!UseDB())
return NS_OK;
if (!mLoadedTemporaryTable)
return NS_OK;
// If we are not forced to flush (e.g. on shutdown) then don't flush if the
// last table access is less then 5 seconds ago or the table itself is not
// older then 30 secs
if (!force &&
((TimeStamp::Now() - mLastTemporaryTableAccessTime).ToSeconds() <
NS_DOMSTORAGE_MAXIMUM_TEMPTABLE_INACTIVITY_TIME) &&
((TimeStamp::Now() - mTemporaryTableAge).ToSeconds() <
NS_DOMSTORAGE_MAXIMUM_TEMPTABLE_AGE))
return NS_OK;
return gStorageDB->FlushAndDeleteTemporaryTableForStorage(this);
#endif
return NS_OK;
}
nsresult
nsDOMStorage::RegisterObservers()
{
nsCOMPtr<nsIObserverService> obsserv = mozilla::services::GetObserverService();
if (obsserv) {
obsserv->AddObserver(this, "profile-before-change", PR_TRUE);
obsserv->AddObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID, PR_TRUE);
obsserv->AddObserver(this, NS_DOMSTORAGE_FLUSH_TIMER_OBSERVER, PR_TRUE);
}
return NS_OK;
}
bool
nsDOMStorage::WasTemporaryTableLoaded()
{
return mLoadedTemporaryTable;
}
void
nsDOMStorage::SetTemporaryTableLoaded(bool loaded)
{
if (loaded) {
mLastTemporaryTableAccessTime = TimeStamp::Now();
if (!mLoadedTemporaryTable)
mTemporaryTableAge = mLastTemporaryTableAccessTime;
}
mLoadedTemporaryTable = loaded;
}
NS_IMETHODIMP
nsDOMStorage::Observe(nsISupports *subject,
const char *topic,
const PRUnichar *data)
{
bool isProfileBeforeChange = !strcmp(topic, "profile-before-change");
bool isXPCOMShutdown = !strcmp(topic, NS_XPCOM_SHUTDOWN_OBSERVER_ID);
bool isFlushTimer = !strcmp(topic, NS_DOMSTORAGE_FLUSH_TIMER_OBSERVER);
if (isXPCOMShutdown || isProfileBeforeChange || isFlushTimer) {
nsresult rv = MaybeCommitTemporaryTable(isXPCOMShutdown || isProfileBeforeChange);
if (NS_FAILED(rv)) {
NS_WARNING("DOMStorage: temporary table commit failed");
}
return NS_OK;
}
NS_WARNING("Unrecognized topic in nsDOMStorage::Observe");
return NS_OK;
}
//
// nsDOMStorage2
//
@ -2131,4 +2246,3 @@ nsDOMStorageEventObsolete::InitStorageEvent(const nsAString& aTypeArg,
return NS_OK;
}

View File

@ -59,6 +59,11 @@
#include "nsIDOMStorageManager.h"
#include "nsCycleCollectionParticipant.h"
#include "nsIObserver.h"
#include "nsITimer.h"
#include "nsWeakReference.h"
#include "mozilla/TimeStamp.h"
#define NS_DOMSTORAGE_FLUSH_TIMER_OBSERVER "domstorage-flush-timer"
#ifdef MOZ_STORAGE
#include "nsDOMStorageDBWrapper.h"
@ -72,6 +77,9 @@ class nsDOMStorage;
class nsIDOMStorage;
class nsDOMStorageItem;
using mozilla::TimeStamp;
using mozilla::TimeDuration;
class nsDOMStorageEntry : public nsVoidPtrHashKey
{
public:
@ -128,19 +136,20 @@ protected:
};
class nsDOMStorage : public nsIDOMStorageObsolete,
public nsPIDOMStorage
public nsPIDOMStorage,
public nsIObserver,
public nsSupportsWeakReference
{
public:
nsDOMStorage();
nsDOMStorage(nsDOMStorage& aThat);
virtual ~nsDOMStorage();
// nsISupports
NS_DECL_CYCLE_COLLECTING_ISUPPORTS
NS_DECL_CYCLE_COLLECTION_CLASS_AMBIGUOUS(nsDOMStorage, nsIDOMStorageObsolete)
// nsIDOMStorageObsolete
NS_DECL_NSIDOMSTORAGEOBSOLETE
NS_DECL_NSIOBSERVER
// Helpers for implementing nsIDOMStorage
nsresult GetItem(const nsAString& key, nsAString& aData);
@ -195,6 +204,13 @@ public:
PRBool
CacheStoragePermissions();
// retrieve the value and secure state corresponding to a key out of storage
// that has been cached in mItems hash table.
nsresult
GetCachedValue(const nsAString& aKey,
nsAString& aValue,
PRBool* aSecure);
// retrieve the value and secure state corresponding to a key out of storage.
nsresult
GetDBValue(const nsAString& aKey,
@ -226,10 +242,17 @@ public:
return static_cast<nsDOMStorage*>(static_cast<nsIDOMStorageObsolete*>(aSupports));
}
nsresult RegisterObservers();
nsresult MaybeCommitTemporaryTable(bool force);
bool WasTemporaryTableLoaded();
void SetTemporaryTableLoaded(bool loaded);
protected:
friend class nsDOMStorageManager;
friend class nsDOMStorage2;
friend class nsDOMStoragePersistentDB;
static nsresult InitDB();
@ -278,6 +301,10 @@ protected:
bool mCanUseChromePersist;
bool mLoadedTemporaryTable;
TimeStamp mLastTemporaryTableAccessTime;
TimeStamp mTemporaryTableAge;
public:
// e.g. "moc.rab.oof.:" or "moc.rab.oof.:http:80" depending
// on association with a domain (globalStorage) or

View File

@ -69,6 +69,17 @@ void ReverseString(const nsCSubstring& source, nsCSubstring& result)
}
}
nsDOMStorageDBWrapper::nsDOMStorageDBWrapper()
{
}
nsDOMStorageDBWrapper::~nsDOMStorageDBWrapper()
{
if (mFlushTimer) {
mFlushTimer->Cancel();
}
}
nsresult
nsDOMStorageDBWrapper::Init()
{
@ -86,9 +97,42 @@ nsDOMStorageDBWrapper::Init()
rv = mPrivateBrowsingDB.Init();
NS_ENSURE_SUCCESS(rv, rv);
mFlushTimer = do_CreateInstance(NS_TIMER_CONTRACTID, &rv);
NS_ENSURE_SUCCESS(rv, rv);
rv = mFlushTimer->Init(nsDOMStorageManager::gStorageManager, 5000,
nsITimer::TYPE_REPEATING_SLACK);
NS_ENSURE_SUCCESS(rv, rv);
return NS_OK;
}
nsresult
nsDOMStorageDBWrapper::EnsureLoadTemporaryTableForStorage(nsDOMStorage* aStorage)
{
if (aStorage->CanUseChromePersist())
return mChromePersistentDB.EnsureLoadTemporaryTableForStorage(aStorage);
if (nsDOMStorageManager::gStorageManager->InPrivateBrowsingMode())
return NS_OK;
if (aStorage->SessionOnly())
return NS_OK;
return mPersistentDB.EnsureLoadTemporaryTableForStorage(aStorage);
}
nsresult
nsDOMStorageDBWrapper::FlushAndDeleteTemporaryTableForStorage(nsDOMStorage* aStorage)
{
if (aStorage->CanUseChromePersist())
return mChromePersistentDB.FlushAndDeleteTemporaryTableForStorage(aStorage);
if (nsDOMStorageManager::gStorageManager->InPrivateBrowsingMode())
return NS_OK;
if (aStorage->SessionOnly())
return NS_OK;
return mPersistentDB.FlushAndDeleteTemporaryTableForStorage(aStorage);
}
nsresult
nsDOMStorageDBWrapper::GetAllKeys(nsDOMStorage* aStorage,
nsTHashtable<nsSessionStorageEntry>* aKeys)

View File

@ -86,12 +86,17 @@ class nsSessionStorageEntry;
class nsDOMStorageDBWrapper
{
public:
nsDOMStorageDBWrapper() {}
~nsDOMStorageDBWrapper() {}
nsDOMStorageDBWrapper();
~nsDOMStorageDBWrapper();
nsresult
Init();
nsresult
EnsureLoadTemporaryTableForStorage(nsDOMStorage* aStorage);
nsresult
FlushAndDeleteTemporaryTableForStorage(nsDOMStorage* aStorage);
/**
* Retrieve a list of all the keys associated with a particular domain.
*/
@ -221,6 +226,8 @@ protected:
nsDOMStoragePersistentDB mPersistentDB;
nsDOMStorageMemoryDB mSessionOnlyDB;
nsDOMStorageMemoryDB mPrivateBrowsingDB;
nsCOMPtr<nsITimer> mFlushTimer;
};
#endif /* nsDOMStorageDB_h___ */

View File

@ -48,6 +48,8 @@
#include "mozStorageCID.h"
#include "mozStorageHelper.h"
#include "mozIStorageService.h"
#include "mozIStorageBindingParamsArray.h"
#include "mozIStorageBindingParams.h"
#include "mozIStorageValueArray.h"
#include "mozIStorageFunction.h"
#include "nsPrintfCString.h"
@ -94,6 +96,10 @@ class nsIsOfflineSQLFunction : public mozIStorageFunction
NS_IMPL_ISUPPORTS1(nsIsOfflineSQLFunction, mozIStorageFunction)
nsDOMStoragePersistentDB::nsDOMStoragePersistentDB()
{
}
NS_IMETHODIMP
nsIsOfflineSQLFunction::OnFunctionCall(
mozIStorageValueArray *aFunctionArguments, nsIVariant **aResult)
@ -123,6 +129,53 @@ nsIsOfflineSQLFunction::OnFunctionCall(
return NS_OK;
}
class Binder
{
public:
Binder(mozIStorageStatement* statement, nsresult *rv);
mozIStorageBindingParams* operator->();
nsresult Add();
private:
mozIStorageStatement* mStmt;
nsCOMPtr<mozIStorageBindingParamsArray> mArray;
nsCOMPtr<mozIStorageBindingParams> mParams;
};
Binder::Binder(mozIStorageStatement* statement, nsresult *rv)
: mStmt(statement)
{
*rv = mStmt->NewBindingParamsArray(getter_AddRefs(mArray));
if (NS_FAILED(*rv))
return;
*rv = mArray->NewBindingParams(getter_AddRefs(mParams));
if (NS_FAILED(*rv))
return;
*rv = NS_OK;
}
mozIStorageBindingParams*
Binder::operator->()
{
return mParams;
}
nsresult
Binder::Add()
{
nsresult rv;
rv = mArray->AddParams(mParams);
NS_ENSURE_SUCCESS(rv, rv);
rv = mStmt->BindParameters(mArray);
NS_ENSURE_SUCCESS(rv, rv);
return rv;
}
nsresult
nsDOMStoragePersistentDB::Init(const nsString& aDatabaseName)
{
@ -148,14 +201,20 @@ nsDOMStoragePersistentDB::Init(const nsString& aDatabaseName)
}
NS_ENSURE_SUCCESS(rv, rv);
rv = mConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
"PRAGMA temp_store = MEMORY"));
NS_ENSURE_SUCCESS(rv, rv);
mozStorageTransaction transaction(mConnection, PR_FALSE);
// Ensure Gecko 1.9.1 storage table
rv = mConnection->ExecuteSimpleSQL(
NS_LITERAL_CSTRING("CREATE TABLE IF NOT EXISTS webappsstore2 ("
"scope TEXT, "
"key TEXT, "
"value TEXT, "
"secure INTEGER, "
"owner TEXT)"));
rv = mConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
"CREATE TABLE IF NOT EXISTS webappsstore2 ("
"scope TEXT, "
"key TEXT, "
"value TEXT, "
"secure INTEGER, "
"owner TEXT)"));
NS_ENSURE_SUCCESS(rv, rv);
rv = mConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
@ -163,6 +222,43 @@ nsDOMStoragePersistentDB::Init(const nsString& aDatabaseName)
" ON webappsstore2(scope, key)"));
NS_ENSURE_SUCCESS(rv, rv);
rv = mConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
"CREATE TEMPORARY TABLE webappsstore2_temp ("
"scope TEXT, "
"key TEXT, "
"value TEXT, "
"secure INTEGER, "
"owner TEXT)"));
NS_ENSURE_SUCCESS(rv, rv);
rv = mConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
"CREATE UNIQUE INDEX scope_key_index_temp"
" ON webappsstore2_temp(scope, key)"));
NS_ENSURE_SUCCESS(rv, rv);
rv = mConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
"CREATE TEMPORARY VIEW webappsstore2_view AS "
"SELECT * FROM webappsstore2_temp "
"UNION ALL "
"SELECT * FROM webappsstore2 "
"WHERE NOT EXISTS ("
"SELECT scope, key FROM webappsstore2_temp "
"WHERE scope = webappsstore2.scope AND key = webappsstore2.key)"));
NS_ENSURE_SUCCESS(rv, rv);
// carry deletion to both the temporary table and the disk table
rv = mConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
"CREATE TEMPORARY TRIGGER webappsstore2_view_delete_trigger "
"INSTEAD OF DELETE ON webappsstore2_view "
"BEGIN "
"DELETE FROM webappsstore2_temp "
"WHERE scope = OLD.scope AND key = OLD.key; "
"DELETE FROM webappsstore2 "
"WHERE scope = OLD.scope AND key = OLD.key; "
"END"));
NS_ENSURE_SUCCESS(rv, rv);
nsCOMPtr<mozIStorageFunction> function1(new nsReverseStringSQLFunction());
NS_ENSURE_TRUE(function1, NS_ERROR_OUT_OF_MEMORY);
@ -218,92 +314,218 @@ nsDOMStoragePersistentDB::Init(const nsString& aDatabaseName)
NS_ENSURE_SUCCESS(rv, rv);
}
// temporary - disk synchronization statements
rv = mConnection->CreateStatement(NS_LITERAL_CSTRING(
"INSERT INTO webappsstore2_temp"
" SELECT * FROM webappsstore2"
" WHERE scope = :scope AND NOT EXISTS ("
"SELECT scope, key FROM webappsstore2_temp "
"WHERE scope = webappsstore2.scope AND key = webappsstore2.key)"),
getter_AddRefs(mCopyToTempTableStatement));
NS_ENSURE_SUCCESS(rv, rv);
rv = mConnection->CreateStatement(NS_LITERAL_CSTRING(
"INSERT OR REPLACE INTO webappsstore2"
" SELECT * FROM webappsstore2_temp"
" WHERE scope = :scope;"),
getter_AddRefs(mCopyBackToDiskStatement));
NS_ENSURE_SUCCESS(rv, rv);
rv = mConnection->CreateStatement(NS_LITERAL_CSTRING(
"DELETE FROM webappsstore2_temp"
" WHERE scope = :scope;"),
getter_AddRefs(mDeleteTemporaryTableStatement));
NS_ENSURE_SUCCESS(rv, rv);
// retrieve all keys associated with a domain
rv = mConnection->CreateStatement(
NS_LITERAL_CSTRING("SELECT key, secure FROM webappsstore2 "
"WHERE scope = ?1"),
rv = mConnection->CreateStatement(NS_LITERAL_CSTRING(
"SELECT key, value, secure FROM webappsstore2_temp "
"WHERE scope = :scope"),
getter_AddRefs(mGetAllKeysStatement));
NS_ENSURE_SUCCESS(rv, rv);
// retrieve a value given a domain and a key
rv = mConnection->CreateStatement(
NS_LITERAL_CSTRING("SELECT value, secure FROM webappsstore2 "
"WHERE scope = ?1 "
"AND key = ?2"),
rv = mConnection->CreateStatement(NS_LITERAL_CSTRING(
"SELECT value, secure FROM webappsstore2_temp "
"WHERE scope = :scope "
"AND key = :key"),
getter_AddRefs(mGetKeyValueStatement));
NS_ENSURE_SUCCESS(rv, rv);
// insert a new key
rv = mConnection->CreateStatement(
NS_LITERAL_CSTRING("INSERT OR REPLACE INTO "
"webappsstore2(scope, key, value, secure) "
"VALUES (?1, ?2, ?3, ?4)"),
rv = mConnection->CreateStatement(NS_LITERAL_CSTRING(
"INSERT OR REPLACE INTO "
"webappsstore2_temp(scope, key, value, secure) "
"VALUES (:scope, :key, :value, :secure)"),
getter_AddRefs(mInsertKeyStatement));
NS_ENSURE_SUCCESS(rv, rv);
// update an existing key
rv = mConnection->CreateStatement(
NS_LITERAL_CSTRING("UPDATE webappsstore2 "
"SET value = ?1, secure = ?2"
"WHERE scope = ?3 "
"AND key = ?4"),
getter_AddRefs(mUpdateKeyStatement));
NS_ENSURE_SUCCESS(rv, rv);
// update the secure status of an existing key
rv = mConnection->CreateStatement(
NS_LITERAL_CSTRING("UPDATE webappsstore2 "
"SET secure = ?1 "
"WHERE scope = ?2 "
"AND key = ?3 "),
rv = mConnection->CreateStatement(NS_LITERAL_CSTRING(
"UPDATE webappsstore2_temp "
"SET secure = :secure "
"WHERE scope = :scope "
"AND key = :key "),
getter_AddRefs(mSetSecureStatement));
NS_ENSURE_SUCCESS(rv, rv);
// remove a key
rv = mConnection->CreateStatement(
NS_LITERAL_CSTRING("DELETE FROM webappsstore2 "
"WHERE scope = ?1 "
"AND key = ?2"),
rv = mConnection->CreateStatement(NS_LITERAL_CSTRING(
"DELETE FROM webappsstore2_view "
"WHERE scope = :scope "
"AND key = :key"),
getter_AddRefs(mRemoveKeyStatement));
NS_ENSURE_SUCCESS(rv, rv);
// remove keys owned by a specific domain
rv = mConnection->CreateStatement(
NS_LITERAL_CSTRING("DELETE FROM webappsstore2 "
"WHERE scope GLOB ?1"),
rv = mConnection->CreateStatement(NS_LITERAL_CSTRING(
"DELETE FROM webappsstore2_view "
"WHERE scope GLOB :scope"),
getter_AddRefs(mRemoveOwnerStatement));
NS_ENSURE_SUCCESS(rv, rv);
// remove keys belonging exactly only to a specific domain
rv = mConnection->CreateStatement(
NS_LITERAL_CSTRING("DELETE FROM webappsstore2 "
"WHERE scope = ?1"),
rv = mConnection->CreateStatement(NS_LITERAL_CSTRING(
"DELETE FROM webappsstore2_view "
"WHERE scope = :scope"),
getter_AddRefs(mRemoveStorageStatement));
NS_ENSURE_SUCCESS(rv, rv);
// remove all keys
rv = mConnection->CreateStatement(
NS_LITERAL_CSTRING("DELETE FROM webappsstore2"),
rv = mConnection->CreateStatement(NS_LITERAL_CSTRING(
"DELETE FROM webappsstore2_view"),
getter_AddRefs(mRemoveAllStatement));
NS_ENSURE_SUCCESS(rv, rv);
// check the usage for a given owner that is an offline-app allowed domain
rv = mConnection->CreateStatement(
NS_LITERAL_CSTRING("SELECT SUM(LENGTH(key) + LENGTH(value)) "
"FROM webappsstore2 "
"WHERE scope GLOB ?1"),
rv = mConnection->CreateStatement(NS_LITERAL_CSTRING(
"SELECT SUM(LENGTH(key) + LENGTH(value)) "
"FROM ("
"SELECT key,value FROM webappsstore2_temp "
"WHERE scope GLOB :scope "
"UNION ALL "
"SELECT key,value FROM webappsstore2 "
"WHERE scope GLOB :scope "
"AND NOT EXISTS ("
"SELECT scope, key "
"FROM webappsstore2_temp "
"WHERE scope = webappsstore2.scope "
"AND key = webappsstore2.key"
")"
")"),
getter_AddRefs(mGetFullUsageStatement));
NS_ENSURE_SUCCESS(rv, rv);
// check the usage for a given owner that is not an offline-app allowed domain
rv = mConnection->CreateStatement(
NS_LITERAL_CSTRING("SELECT SUM(LENGTH(key) + LENGTH(value)) "
"FROM webappsstore2 "
"WHERE scope GLOB ?1 "
"AND NOT ISOFFLINE(scope)"),
rv = mConnection->CreateStatement(NS_LITERAL_CSTRING(
"SELECT SUM(LENGTH(key) + LENGTH(value)) "
"FROM ("
"SELECT key, value FROM webappsstore2_temp "
"WHERE scope GLOB :scope "
"AND NOT ISOFFLINE(scope) "
"UNION ALL "
"SELECT key, value FROM webappsstore2 "
"WHERE scope GLOB :scope "
"AND NOT ISOFFLINE(scope) "
"AND NOT EXISTS ("
"SELECT scope, key "
"FROM webappsstore2_temp "
"WHERE scope = webappsstore2.scope "
"AND key = webappsstore2.key"
")"
")"),
getter_AddRefs(mGetOfflineExcludedUsageStatement));
NS_ENSURE_SUCCESS(rv, rv);
rv = transaction.Commit();
NS_ENSURE_SUCCESS(rv, rv);
return NS_OK;
}
nsresult
nsDOMStoragePersistentDB::EnsureLoadTemporaryTableForStorage(nsDOMStorage* aStorage)
{
if (!aStorage->WasTemporaryTableLoaded()) {
nsresult rv;
rv = MaybeCommitInsertTransaction();
NS_ENSURE_SUCCESS(rv, rv);
mozStorageStatementScoper scope(mCopyToTempTableStatement);
Binder binder(mCopyToTempTableStatement, &rv);
NS_ENSURE_SUCCESS(rv, rv);
rv = binder->BindUTF8StringByName(NS_LITERAL_CSTRING("scope"),
aStorage->GetScopeDBKey());
NS_ENSURE_SUCCESS(rv, rv);
rv = binder.Add();
NS_ENSURE_SUCCESS(rv, rv);
rv = mCopyToTempTableStatement->Execute();
NS_ENSURE_SUCCESS(rv, rv);
}
// Always call this to update the last access time
aStorage->SetTemporaryTableLoaded(true);
return NS_OK;
}
nsresult
nsDOMStoragePersistentDB::FlushAndDeleteTemporaryTableForStorage(nsDOMStorage* aStorage)
{
if (!aStorage->WasTemporaryTableLoaded())
return NS_OK;
mozStorageTransaction trans(mConnection, PR_FALSE);
nsresult rv;
{
mozStorageStatementScoper scope(mCopyBackToDiskStatement);
Binder binder(mCopyBackToDiskStatement, &rv);
NS_ENSURE_SUCCESS(rv, rv);
rv = binder->BindUTF8StringByName(NS_LITERAL_CSTRING("scope"),
aStorage->GetScopeDBKey());
NS_ENSURE_SUCCESS(rv, rv);
rv = binder.Add();
NS_ENSURE_SUCCESS(rv, rv);
rv = mCopyBackToDiskStatement->Execute();
NS_ENSURE_SUCCESS(rv, rv);
}
{
mozStorageStatementScoper scope(mDeleteTemporaryTableStatement);
Binder binder(mDeleteTemporaryTableStatement, &rv);
NS_ENSURE_SUCCESS(rv, rv);
rv = binder->BindUTF8StringByName(NS_LITERAL_CSTRING("scope"),
aStorage->GetScopeDBKey());
NS_ENSURE_SUCCESS(rv, rv);
rv = binder.Add();
NS_ENSURE_SUCCESS(rv, rv);
rv = mDeleteTemporaryTableStatement->Execute();
NS_ENSURE_SUCCESS(rv, rv);
}
rv = trans.Commit();
NS_ENSURE_SUCCESS(rv, rv);
rv = MaybeCommitInsertTransaction();
NS_ENSURE_SUCCESS(rv, rv);
aStorage->SetTemporaryTableLoaded(false);
return NS_OK;
}
@ -311,9 +533,24 @@ nsresult
nsDOMStoragePersistentDB::GetAllKeys(nsDOMStorage* aStorage,
nsTHashtable<nsSessionStorageEntry>* aKeys)
{
nsresult rv;
rv = MaybeCommitInsertTransaction();
NS_ENSURE_SUCCESS(rv, rv);
rv = EnsureLoadTemporaryTableForStorage(aStorage);
NS_ENSURE_SUCCESS(rv, rv);
mozStorageStatementScoper scope(mGetAllKeysStatement);
nsresult rv = mGetAllKeysStatement->BindUTF8StringParameter(0, aStorage->GetScopeDBKey());
Binder binder(mGetAllKeysStatement, &rv);
NS_ENSURE_SUCCESS(rv, rv);
rv = binder->BindUTF8StringByName(NS_LITERAL_CSTRING("scope"),
aStorage->GetScopeDBKey());
NS_ENSURE_SUCCESS(rv, rv);
rv = binder.Add();
NS_ENSURE_SUCCESS(rv, rv);
PRBool exists;
@ -324,14 +561,18 @@ nsDOMStoragePersistentDB::GetAllKeys(nsDOMStorage* aStorage,
rv = mGetAllKeysStatement->GetString(0, key);
NS_ENSURE_SUCCESS(rv, rv);
nsAutoString value;
rv = mGetAllKeysStatement->GetString(1, value);
NS_ENSURE_SUCCESS(rv, rv);
PRInt32 secureInt = 0;
rv = mGetAllKeysStatement->GetInt32(1, &secureInt);
rv = mGetAllKeysStatement->GetInt32(2, &secureInt);
NS_ENSURE_SUCCESS(rv, rv);
nsSessionStorageEntry* entry = aKeys->PutEntry(key);
NS_ENSURE_TRUE(entry, NS_ERROR_OUT_OF_MEMORY);
entry->mItem = new nsDOMStorageItem(aStorage, key, EmptyString(), secureInt);
entry->mItem = new nsDOMStorageItem(aStorage, key, value, secureInt);
if (!entry->mItem) {
aKeys->RawRemoveEntry(entry);
return NS_ERROR_OUT_OF_MEMORY;
@ -347,12 +588,27 @@ nsDOMStoragePersistentDB::GetKeyValue(nsDOMStorage* aStorage,
nsAString& aValue,
PRBool* aSecure)
{
nsresult rv;
rv = MaybeCommitInsertTransaction();
NS_ENSURE_SUCCESS(rv, rv);
rv = EnsureLoadTemporaryTableForStorage(aStorage);
NS_ENSURE_SUCCESS(rv, rv);
mozStorageStatementScoper scope(mGetKeyValueStatement);
nsresult rv = mGetKeyValueStatement->BindUTF8StringParameter(
0, aStorage->GetScopeDBKey());
Binder binder(mGetKeyValueStatement, &rv);
NS_ENSURE_SUCCESS(rv, rv);
rv = mGetKeyValueStatement->BindStringParameter(1, aKey);
rv = binder->BindUTF8StringByName(NS_LITERAL_CSTRING("scope"),
aStorage->GetScopeDBKey());
NS_ENSURE_SUCCESS(rv, rv);
rv = binder->BindStringByName(NS_LITERAL_CSTRING("key"),
aKey);
NS_ENSURE_SUCCESS(rv, rv);
rv = binder.Add();
NS_ENSURE_SUCCESS(rv, rv);
PRBool exists;
@ -385,84 +641,61 @@ nsDOMStoragePersistentDB::SetKey(nsDOMStorage* aStorage,
PRBool aExcludeOfflineFromUsage,
PRInt32 *aNewUsage)
{
mozStorageStatementScoper scope(mGetKeyValueStatement);
nsresult rv;
rv = EnsureLoadTemporaryTableForStorage(aStorage);
NS_ENSURE_SUCCESS(rv, rv);
PRInt32 usage = 0;
nsresult rv;
if (!aStorage->GetQuotaDomainDBKey(!aExcludeOfflineFromUsage).IsEmpty()) {
rv = GetUsage(aStorage, aExcludeOfflineFromUsage, &usage);
NS_ENSURE_SUCCESS(rv, rv);
}
aStorage->CacheKeysFromDB();
usage += aKey.Length() + aValue.Length();
rv = mGetKeyValueStatement->BindUTF8StringParameter(0,
aStorage->GetScopeDBKey());
NS_ENSURE_SUCCESS(rv, rv);
rv = mGetKeyValueStatement->BindStringParameter(1, aKey);
NS_ENSURE_SUCCESS(rv, rv);
PRBool exists;
rv = mGetKeyValueStatement->ExecuteStep(&exists);
NS_ENSURE_SUCCESS(rv, rv);
if (exists) {
if (!aSecure) {
PRInt32 secureInt = 0;
rv = mGetKeyValueStatement->GetInt32(1, &secureInt);
NS_ENSURE_SUCCESS(rv, rv);
if (secureInt)
return NS_ERROR_DOM_SECURITY_ERR;
}
nsAutoString previousValue;
rv = mGetKeyValueStatement->GetString(0, previousValue);
NS_ENSURE_SUCCESS(rv, rv);
nsAutoString previousValue;
PRBool secure;
rv = aStorage->GetCachedValue(aKey, previousValue, &secure);
if (NS_SUCCEEDED(rv)) {
if (!aSecure && secure)
return NS_ERROR_DOM_SECURITY_ERR;
usage -= aKey.Length() + previousValue.Length();
mGetKeyValueStatement->Reset();
if (usage > aQuota) {
return NS_ERROR_DOM_QUOTA_REACHED;
}
mozStorageStatementScoper scopeupdate(mUpdateKeyStatement);
rv = mUpdateKeyStatement->BindStringParameter(0, aValue);
NS_ENSURE_SUCCESS(rv, rv);
rv = mUpdateKeyStatement->BindInt32Parameter(1, aSecure);
NS_ENSURE_SUCCESS(rv, rv);
rv = mUpdateKeyStatement->BindUTF8StringParameter(2,
aStorage->GetScopeDBKey());
NS_ENSURE_SUCCESS(rv, rv);
rv = mUpdateKeyStatement->BindStringParameter(3, aKey);
NS_ENSURE_SUCCESS(rv, rv);
rv = mUpdateKeyStatement->Execute();
NS_ENSURE_SUCCESS(rv, rv);
}
else {
if (usage > aQuota) {
return NS_ERROR_DOM_QUOTA_REACHED;
}
mozStorageStatementScoper scopeinsert(mInsertKeyStatement);
rv = mInsertKeyStatement->BindUTF8StringParameter(0,
aStorage->GetScopeDBKey());
NS_ENSURE_SUCCESS(rv, rv);
rv = mInsertKeyStatement->BindStringParameter(1, aKey);
NS_ENSURE_SUCCESS(rv, rv);
rv = mInsertKeyStatement->BindStringParameter(2, aValue);
NS_ENSURE_SUCCESS(rv, rv);
rv = mInsertKeyStatement->BindInt32Parameter(3, aSecure);
NS_ENSURE_SUCCESS(rv, rv);
rv = mInsertKeyStatement->Execute();
NS_ENSURE_SUCCESS(rv, rv);
if (usage > aQuota) {
return NS_ERROR_DOM_QUOTA_REACHED;
}
rv = EnsureInsertTransaction();
NS_ENSURE_SUCCESS(rv, rv);
mozStorageStatementScoper scopeinsert(mInsertKeyStatement);
Binder binder(mInsertKeyStatement, &rv);
NS_ENSURE_SUCCESS(rv, rv);
rv = binder->BindUTF8StringByName(NS_LITERAL_CSTRING("scope"),
aStorage->GetScopeDBKey());
NS_ENSURE_SUCCESS(rv, rv);
rv = binder->BindStringByName(NS_LITERAL_CSTRING("key"),
aKey);
NS_ENSURE_SUCCESS(rv, rv);
rv = binder->BindStringByName(NS_LITERAL_CSTRING("value"),
aValue);
NS_ENSURE_SUCCESS(rv, rv);
rv = binder->BindInt32ByName(NS_LITERAL_CSTRING("secure"),
aSecure ? 1 : 0);
NS_ENSURE_SUCCESS(rv, rv);
rv = binder.Add();
NS_ENSURE_SUCCESS(rv, rv);
rv = mInsertKeyStatement->Execute();
NS_ENSURE_SUCCESS(rv, rv);
if (!aStorage->GetQuotaDomainDBKey(!aExcludeOfflineFromUsage).IsEmpty()) {
mCachedOwner = aStorage->GetQuotaDomainDBKey(!aExcludeOfflineFromUsage);
mCachedUsage = usage;
@ -480,13 +713,28 @@ nsDOMStoragePersistentDB::SetSecure(nsDOMStorage* aStorage,
{
nsresult rv;
rv = EnsureLoadTemporaryTableForStorage(aStorage);
NS_ENSURE_SUCCESS(rv, rv);
rv = EnsureInsertTransaction();
NS_ENSURE_SUCCESS(rv, rv);
mozStorageStatementScoper scope(mSetSecureStatement);
rv = mSetSecureStatement->BindInt32Parameter(0, aSecure ? 1 : 0);
Binder binder(mSetSecureStatement, &rv);
NS_ENSURE_SUCCESS(rv, rv);
rv = mSetSecureStatement->BindUTF8StringParameter(1, aStorage->GetScopeDBKey());
rv = binder->BindUTF8StringByName(NS_LITERAL_CSTRING("scope"),
aStorage->GetScopeDBKey());
NS_ENSURE_SUCCESS(rv, rv);
rv = mSetSecureStatement->BindStringParameter(2, aKey);
rv = binder->BindStringByName(NS_LITERAL_CSTRING("key"),
aKey);
NS_ENSURE_SUCCESS(rv, rv);
rv = binder->BindInt32ByName(NS_LITERAL_CSTRING("secure"),
aSecure ? 1 : 0);
NS_ENSURE_SUCCESS(rv, rv);
rv = binder.Add();
NS_ENSURE_SUCCESS(rv, rv);
return mSetSecureStatement->Execute();
@ -498,42 +746,74 @@ nsDOMStoragePersistentDB::RemoveKey(nsDOMStorage* aStorage,
PRBool aExcludeOfflineFromUsage,
PRInt32 aKeyUsage)
{
nsresult rv;
rv = MaybeCommitInsertTransaction();
NS_ENSURE_SUCCESS(rv, rv);
mozStorageStatementScoper scope(mRemoveKeyStatement);
if (aStorage->GetQuotaDomainDBKey(!aExcludeOfflineFromUsage) == mCachedOwner) {
mCachedUsage -= aKeyUsage;
}
nsresult rv = mRemoveKeyStatement->BindUTF8StringParameter(
0, aStorage->GetScopeDBKey());
NS_ENSURE_SUCCESS(rv, rv);
rv = mRemoveKeyStatement->BindStringParameter(1, aKey);
Binder binder(mRemoveKeyStatement, &rv);
NS_ENSURE_SUCCESS(rv, rv);
return mRemoveKeyStatement->Execute();
rv = binder->BindUTF8StringByName(NS_LITERAL_CSTRING("scope"),
aStorage->GetScopeDBKey());
NS_ENSURE_SUCCESS(rv, rv);
rv = binder->BindStringByName(NS_LITERAL_CSTRING("key"),
aKey);
NS_ENSURE_SUCCESS(rv, rv);
rv = binder.Add();
NS_ENSURE_SUCCESS(rv, rv);
rv = mRemoveKeyStatement->Execute();
NS_ENSURE_SUCCESS(rv, rv);
return NS_OK;
}
nsresult
nsDOMStoragePersistentDB::ClearStorage(nsDOMStorage* aStorage)
{
nsresult rv;
rv = MaybeCommitInsertTransaction();
NS_ENSURE_SUCCESS(rv, rv);
mozStorageStatementScoper scope(mRemoveStorageStatement);
mCachedUsage = 0;
mCachedOwner.Truncate();
nsresult rv;
rv = mRemoveStorageStatement->BindUTF8StringParameter(
0, aStorage->GetScopeDBKey());
Binder binder(mRemoveStorageStatement, &rv);
NS_ENSURE_SUCCESS(rv, rv);
return mRemoveStorageStatement->Execute();
rv = binder->BindUTF8StringByName(NS_LITERAL_CSTRING("scope"),
aStorage->GetScopeDBKey());
NS_ENSURE_SUCCESS(rv, rv);
rv = binder.Add();
NS_ENSURE_SUCCESS(rv, rv);
rv = mRemoveStorageStatement->Execute();
NS_ENSURE_SUCCESS(rv, rv);
return NS_OK;
}
nsresult
nsDOMStoragePersistentDB::RemoveOwner(const nsACString& aOwner,
PRBool aIncludeSubDomains)
{
nsresult rv;
rv = MaybeCommitInsertTransaction();
NS_ENSURE_SUCCESS(rv, rv);
mozStorageStatementScoper scope(mRemoveOwnerStatement);
nsCAutoString subdomainsDBKey;
@ -548,12 +828,20 @@ nsDOMStoragePersistentDB::RemoveOwner(const nsACString& aOwner,
mCachedOwner.Truncate();
}
nsresult rv;
rv = mRemoveOwnerStatement->BindUTF8StringParameter(0, subdomainsDBKey);
Binder binder(mRemoveOwnerStatement, &rv);
NS_ENSURE_SUCCESS(rv, rv);
return mRemoveOwnerStatement->Execute();
rv = binder->BindUTF8StringByName(NS_LITERAL_CSTRING("scope"),
subdomainsDBKey);
NS_ENSURE_SUCCESS(rv, rv);
rv = binder.Add();
NS_ENSURE_SUCCESS(rv, rv);
rv = mRemoveOwnerStatement->Execute();
NS_ENSURE_SUCCESS(rv, rv);
return NS_OK;
}
@ -574,9 +862,9 @@ nsDOMStoragePersistentDB::RemoveOwners(const nsTArray<nsString> &aOwners,
nsCString expression;
if (aMatch) {
expression.AppendLiteral("DELETE FROM webappsstore2 WHERE scope IN (");
expression.AppendLiteral("DELETE FROM webappsstore2_view WHERE scope IN (");
} else {
expression.AppendLiteral("DELETE FROM webappsstore2 WHERE scope NOT IN (");
expression.AppendLiteral("DELETE FROM webappsstore2_view WHERE scope NOT IN (");
}
for (PRUint32 i = 0; i < aOwners.Length(); i++) {
@ -584,14 +872,27 @@ nsDOMStoragePersistentDB::RemoveOwners(const nsTArray<nsString> &aOwners,
expression.AppendLiteral(" UNION ");
expression.AppendLiteral(
"SELECT DISTINCT scope FROM webappsstore2 WHERE scope GLOB ?");
"SELECT DISTINCT scope FROM webappsstore2_temp WHERE scope GLOB :scope");
expression.AppendInt(i);
expression.AppendLiteral(" UNION ");
expression.AppendLiteral(
"SELECT DISTINCT scope FROM webappsstore2 WHERE scope GLOB :scope");
expression.AppendInt(i);
}
expression.AppendLiteral(");");
nsCOMPtr<mozIStorageStatement> statement;
nsresult rv = mConnection->CreateStatement(expression,
getter_AddRefs(statement));
nsresult rv;
rv = MaybeCommitInsertTransaction();
NS_ENSURE_SUCCESS(rv, rv);
rv = mConnection->CreateStatement(expression,
getter_AddRefs(statement));
NS_ENSURE_SUCCESS(rv, rv);
Binder binder(statement, &rv);
NS_ENSURE_SUCCESS(rv, rv);
for (PRUint32 i = 0; i < aOwners.Length(); i++) {
@ -603,10 +904,17 @@ nsDOMStoragePersistentDB::RemoveOwners(const nsTArray<nsString> &aOwners,
quotaKey.AppendLiteral(":");
quotaKey.AppendLiteral("*");
rv = statement->BindUTF8StringParameter(i, quotaKey);
nsCAutoString paramName;
paramName.Assign("scope");
paramName.AppendInt(i);
rv = binder->BindUTF8StringByName(paramName, quotaKey);
NS_ENSURE_SUCCESS(rv, rv);
}
rv = binder.Add();
NS_ENSURE_SUCCESS(rv, rv);
rv = statement->Execute();
NS_ENSURE_SUCCESS(rv, rv);
@ -616,8 +924,17 @@ nsDOMStoragePersistentDB::RemoveOwners(const nsTArray<nsString> &aOwners,
nsresult
nsDOMStoragePersistentDB::RemoveAll()
{
nsresult rv;
rv = MaybeCommitInsertTransaction();
NS_ENSURE_SUCCESS(rv, rv);
mozStorageStatementScoper scope(mRemoveAllStatement);
return mRemoveAllStatement->Execute();
rv = mRemoveAllStatement->Execute();
NS_ENSURE_SUCCESS(rv, rv);
return NS_OK;
}
nsresult
@ -657,17 +974,26 @@ nsDOMStoragePersistentDB::GetUsageInternal(const nsACString& aQuotaDomainDBKey,
return NS_OK;
}
nsresult rv;
rv = MaybeCommitInsertTransaction();
NS_ENSURE_SUCCESS(rv, rv);
mozIStorageStatement* statement = aExcludeOfflineFromUsage
? mGetOfflineExcludedUsageStatement : mGetFullUsageStatement;
mozStorageStatementScoper scope(statement);
nsresult rv;
nsCAutoString scopeValue(aQuotaDomainDBKey);
scopeValue += NS_LITERAL_CSTRING("*");
rv = statement->BindUTF8StringParameter(0, scopeValue);
Binder binder(statement, &rv);
NS_ENSURE_SUCCESS(rv, rv);
rv = binder->BindUTF8StringByName(NS_LITERAL_CSTRING("scope"), scopeValue);
NS_ENSURE_SUCCESS(rv, rv);
rv = binder.Add();
NS_ENSURE_SUCCESS(rv, rv);
PRBool exists;
@ -689,3 +1015,44 @@ nsDOMStoragePersistentDB::GetUsageInternal(const nsACString& aQuotaDomainDBKey,
return NS_OK;
}
nsresult
nsDOMStoragePersistentDB::EnsureInsertTransaction()
{
if (!mConnection)
return NS_ERROR_UNEXPECTED;
PRBool transactionInProgress;
nsresult rv = mConnection->GetTransactionInProgress(&transactionInProgress);
NS_ENSURE_SUCCESS(rv, rv);
if (transactionInProgress)
return NS_OK;
rv = mConnection->BeginTransaction();
NS_ENSURE_SUCCESS(rv, rv);
return NS_OK;
}
nsresult
nsDOMStoragePersistentDB::MaybeCommitInsertTransaction()
{
if (!mConnection)
return NS_ERROR_UNEXPECTED;
PRBool transactionInProgress;
nsresult rv = mConnection->GetTransactionInProgress(&transactionInProgress);
if (NS_FAILED(rv)) {
NS_WARNING("nsDOMStoragePersistentDB::MaybeCommitInsertTransaction: "
"connection probably already dead");
}
if (NS_FAILED(rv) || !transactionInProgress)
return NS_OK;
rv = mConnection->CommitTransaction();
NS_ENSURE_SUCCESS(rv, rv);
return NS_OK;
}

View File

@ -50,12 +50,17 @@ class nsSessionStorageEntry;
class nsDOMStoragePersistentDB
{
public:
nsDOMStoragePersistentDB() {}
nsDOMStoragePersistentDB();
~nsDOMStoragePersistentDB() {}
nsresult
Init(const nsString& aDatabaseName);
nsresult
EnsureLoadTemporaryTableForStorage(nsDOMStorage* aStorage);
nsresult
FlushAndDeleteTemporaryTableForStorage(nsDOMStorage* aStorage);
/**
* Retrieve a list of all the keys associated with a particular domain.
*/
@ -146,14 +151,27 @@ public:
*/
nsresult ClearAllPrivateBrowsingData();
/**
* We process INSERTs in a transaction because of performance.
* If there is currently no transaction in progress, start one.
*/
nsresult EnsureInsertTransaction();
/**
* If there is an INSERT transaction in progress, commit it now.
*/
nsresult MaybeCommitInsertTransaction();
protected:
nsCOMPtr<mozIStorageConnection> mConnection;
nsCOMPtr<mozIStorageStatement> mCopyToTempTableStatement;
nsCOMPtr<mozIStorageStatement> mCopyBackToDiskStatement;
nsCOMPtr<mozIStorageStatement> mDeleteTemporaryTableStatement;
nsCOMPtr<mozIStorageStatement> mGetAllKeysStatement;
nsCOMPtr<mozIStorageStatement> mGetKeyValueStatement;
nsCOMPtr<mozIStorageStatement> mInsertKeyStatement;
nsCOMPtr<mozIStorageStatement> mUpdateKeyStatement;
nsCOMPtr<mozIStorageStatement> mSetSecureStatement;
nsCOMPtr<mozIStorageStatement> mRemoveKeyStatement;
nsCOMPtr<mozIStorageStatement> mRemoveOwnerStatement;

View File

@ -2278,7 +2278,7 @@ NS_IMETHODIMP nsEditor::ScrollSelectionIntoView(PRBool aScrollToAnchor)
region = nsISelectionController::SELECTION_ANCHOR_REGION;
selCon->ScrollSelectionIntoView(nsISelectionController::SELECTION_NORMAL,
region, PR_FALSE);
region, 0);
}
return NS_OK;

View File

@ -1203,7 +1203,8 @@ nsTextServicesDocument::ScrollSelectionIntoView()
// After ScrollSelectionIntoView(), the pending notifications might be flushed
// and PresShell/PresContext/Frames may be dead. See bug 418470.
result = mSelCon->ScrollSelectionIntoView(nsISelectionController::SELECTION_NORMAL, nsISelectionController::SELECTION_FOCUS_REGION, PR_TRUE);
result = mSelCon->ScrollSelectionIntoView(nsISelectionController::SELECTION_NORMAL, nsISelectionController::SELECTION_FOCUS_REGION,
nsISelectionController::SCROLL_SYNCHRONOUS);
UNLOCK_DOC(this);

View File

@ -266,13 +266,15 @@ public class CrashReporter extends Activity
void doRestart()
{
try {
String action = "org.mozilla.gecko.restart@MOZ_APP_NAME@";
String amCmd = "/system/bin/am broadcast -a " + action +
" -n org.mozilla.@MOZ_APP_NAME@/org.mozilla.@MOZ_APP_NAME@.Restarter";
Log.i("GeckoCrashReporter", amCmd);
Runtime.getRuntime().exec(amCmd);
String action = "android.intent.action.MAIN";
Intent intent = new Intent(action);
intent.setClassName("org.mozilla.@MOZ_APP_NAME@",
"org.mozilla.@MOZ_APP_NAME@.App");
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
Log.i("GeckoCrashReporter", intent.toString());
startActivity(intent);
} catch (Exception e) {
Log.i("GeckoCrashReporter", e.toString());
Log.e("GeckoCrashReporter", "error while trying to restart", e);
}
}

View File

@ -426,7 +426,7 @@ abstract public class GeckoApp
outFile.setLastModified(fileEntry.getTime());
}
public String getEnvString() {
public void addEnvToIntent(Intent intent) {
Map<String,String> envMap = System.getenv();
Set<Map.Entry<String,String>> envSet = envMap.entrySet();
Iterator<Map.Entry<String,String>> envIter = envSet.iterator();
@ -434,39 +434,25 @@ abstract public class GeckoApp
int c = 0;
while (envIter.hasNext()) {
Map.Entry<String,String> entry = envIter.next();
// No need to pass env vars that we know the system provides
// Unnecessary vars need to be trimmed since amount of data
// we can pass this way is limited
if (!entry.getKey().equals("BOOTCLASSPATH") &&
!entry.getKey().equals("ANDROID_SOCKET_zygote") &&
!entry.getKey().equals("TMPDIR") &&
!entry.getKey().equals("ANDROID_BOOTLOGO") &&
!entry.getKey().equals("EXTERNAL_STORAGE") &&
!entry.getKey().equals("ANDROID_ASSETS") &&
!entry.getKey().equals("PATH") &&
!entry.getKey().equals("TERMINFO") &&
!entry.getKey().equals("LD_LIBRARY_PATH") &&
!entry.getKey().equals("ANDROID_DATA") &&
!entry.getKey().equals("ANDROID_PROPERTY_WORKSPACE") &&
!entry.getKey().equals("ANDROID_ROOT")) {
envstr.append(" --es env" + c + " " + entry.getKey() + "="
+ entry.getValue());
c++;
}
intent.putExtra("env" + c, entry.getKey() + "="
+ entry.getValue());
c++;
}
return envstr.toString();
}
public void doRestart() {
try {
String action = "org.mozilla.gecko.restart" + getAppName();
String amCmd = "/system/bin/am broadcast -a " + action + getEnvString() + " -n org.mozilla." + getAppName() + "/org.mozilla." + getAppName() + ".Restarter";
Log.i("GeckoAppJava", amCmd);
Runtime.getRuntime().exec(amCmd);
Intent intent = new Intent(action);
intent.setClassName("org.mozilla." + getAppName(),
"org.mozilla." + getAppName() + ".Restarter");
addEnvToIntent(intent);
Log.i("GeckoAppJava", intent.toString());
sendBroadcast(intent);
} catch (Exception e) {
Log.i("GeckoAppJava", e.toString());
}
System.exit(0);
finish();
}
public void handleNotification(String action, String alertName, String alertCookie) {

View File

@ -70,6 +70,7 @@ class GeckoAppShell
private GeckoAppShell() { }
static boolean sGeckoRunning;
static private GeckoEvent gPendingResize = null;
static private boolean gRestartScheduled = false;
@ -90,7 +91,6 @@ class GeckoAppShell
public static native void nativeRun(String args);
// helper methods
public static native void setInitialSize(int width, int height);
public static native void setSurfaceView(GeckoSurfaceView sv);
public static native void putenv(String map);
public static native void onResume();
@ -136,8 +136,6 @@ class GeckoAppShell
// Tell Gecko where the target surface view is for rendering
GeckoAppShell.setSurfaceView(GeckoApp.surfaceView);
sGeckoRunning = true;
// First argument is the .apk path
String combinedArgs = apkPath + " -omnijar " + apkPath;
if (args != null)
@ -151,8 +149,16 @@ class GeckoAppShell
private static GeckoEvent mLastDrawEvent;
public static void sendEventToGecko(GeckoEvent e) {
if (sGeckoRunning)
if (sGeckoRunning) {
if (gPendingResize != null) {
notifyGeckoOfEvent(gPendingResize);
gPendingResize = null;
}
notifyGeckoOfEvent(e);
} else {
if (e.mType == GeckoEvent.SIZE_CHANGED)
gPendingResize = e;
}
}
// Tell the Gecko event loop that an event is available.
@ -325,6 +331,15 @@ class GeckoAppShell
}
}
static void onAppShellReady()
{
sGeckoRunning = true;
if (gPendingResize != null) {
notifyGeckoOfEvent(gPendingResize);
gPendingResize = null;
}
}
static void onXreExit() {
sGeckoRunning = false;
Log.i("GeckoAppJava", "XRE exited");
@ -332,7 +347,7 @@ class GeckoAppShell
GeckoApp.mAppContext.doRestart();
} else {
Log.i("GeckoAppJava", "we're done, good bye");
System.exit(0);
GeckoApp.mAppContext.finish();
}
}

View File

@ -116,9 +116,6 @@ class GeckoSurfaceView
Log.i("GeckoAppJava", "surfaceChanged: fmt: " + format + " dim: " + width + " " + height);
if (!GeckoAppShell.sGeckoRunning)
return;
GeckoEvent e = new GeckoEvent(GeckoEvent.SIZE_CHANGED, width, height, -1, -1);
GeckoAppShell.sendEventToGecko(e);
@ -126,8 +123,6 @@ class GeckoSurfaceView
GeckoAppShell.scheduleRedraw();
mSurfaceNeedsRedraw = false;
}
mSurfaceChanged = true;
} finally {
mSurfaceLock.unlock();
}
@ -195,8 +190,6 @@ class GeckoSurfaceView
Log.e("GeckoAppJava", "endDrawing with false mSurfaceValid");
return;
}
} catch (java.lang.IllegalArgumentException ex) {
mSurfaceChanged = true;
} finally {
mInDrawing = false;
@ -291,10 +284,6 @@ class GeckoSurfaceView
// Do we need to force a redraw on surfaceChanged?
boolean mSurfaceNeedsRedraw;
// Has this surface been changed? (That is,
// do we need to recreate buffers?)
boolean mSurfaceChanged;
// Are we actively between beginDrawing/endDrawing?
boolean mInDrawing;

View File

@ -1,4 +1,4 @@
/* -*- Mode: Java; tab-width: 20; indent-tabs-mode: nil; -*-
/* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*-
* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
*
@ -39,24 +39,25 @@
package org.mozilla.@MOZ_APP_NAME@;
import android.content.*;
import android.util.Log;
import android.util.*;
import android.os.*;
import java.io.*;
public class Restarter extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
@Override
public void onReceive(Context aContext, Intent aIntent) {
Log.i("Restarter", "trying to restart @MOZ_APP_NAME@");
try {
try {
boolean stillRunning;
do {
stillRunning = false;
Process p = Runtime.getRuntime().exec("/system/bin/ps");
java.lang.Process p = Runtime.getRuntime().exec("/system/bin/ps");
BufferedReader psOut = new BufferedReader(new InputStreamReader(p.getInputStream()));
String line;
Log.i("Restarter", "pid: " + new Integer(android.os.Process.myPid()).toString());
while((line = psOut.readLine()) != null) {
Log.i("Restarter", "ps: " + line);
if (line.contains("org.mozilla.@MOZ_APP_NAME@.App")){
if (line.contains("org.mozilla.@MOZ_APP_NAME@")){
if (!line.contains(new Integer(android.os.Process.myPid()).toString())){
Log.i("Restarter", "app still running, wait a bit");
stillRunning = true;
@ -69,23 +70,22 @@ public class Restarter extends BroadcastReceiver {
}
} while(stillRunning);
} catch (Exception e) {
Log.i("Restarter", e.toString());
Log.i("Restarter", e.toString());
}
try {
String action = "android.intent.action.MAIN";
String env;
StringBuffer envstr = new StringBuffer();
for (int c = 0; (env = intent.getStringExtra("env" + c)) != null; c++) {
envstr.append(" --es env" + c + " " + env);
}
String amCmd = "/system/bin/am start -a " + action + envstr.toString() +
" -n org.mozilla.@MOZ_APP_NAME@/org.mozilla.@MOZ_APP_NAME@.App";
Log.i("Restarter", amCmd);
Runtime.getRuntime().exec(amCmd);
Intent intent = new Intent(action);
intent.setClassName("org.mozilla.@MOZ_APP_NAME@",
"org.mozilla.@MOZ_APP_NAME@.App");
Bundle b = aIntent.getExtras();
if (b != null)
intent.putExtras(b);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
Log.i("GeckoAppJava", intent.toString());
aContext.startActivity(intent);
} catch (Exception e) {
Log.i("Restarter", e.toString());
}
System.exit(0);
System.exit(0);
}
};

View File

@ -451,7 +451,8 @@ void nsWebBrowserFind::SetSelectionAndScroll(nsIDOMWindow* aWindow,
// flushed and PresShell/PresContext/Frames may be dead. See bug 418470.
selCon->ScrollSelectionIntoView
(nsISelectionController::SELECTION_NORMAL,
nsISelectionController::SELECTION_FOCUS_REGION, PR_TRUE);
nsISelectionController::SELECTION_FOCUS_REGION,
nsISelectionController::SCROLL_SYNCHRONOUS);
}
}

View File

@ -81,10 +81,6 @@
@QT_SURFACE_FEATURE@
@DDRAW_SURFACE_FEATURE@
@OGLES_SURFACE_FEATURE@
@FT_FONT_FEATURE@
@WIN32_FONT_FEATURE@

View File

@ -390,16 +390,6 @@ ClipToContain(gfxContext* aContext, const nsIntRect& aRect)
aContext->SetMatrix(currentMatrix);
}
static void
InheritContextFlags(gfxContext* aSource, gfxContext* aDest)
{
if (aSource->GetFlags() & gfxContext::FLAG_DESTINED_FOR_SCREEN) {
aDest->SetFlag(gfxContext::FLAG_DESTINED_FOR_SCREEN);
} else {
aDest->ClearFlag(gfxContext::FLAG_DESTINED_FOR_SCREEN);
}
}
static PRBool
ShouldRetainTransparentSurface(PRUint32 aContentFlags,
gfxASurface* aTargetSurface)
@ -493,7 +483,6 @@ BasicThebesLayer::Paint(gfxContext* aContext,
// from RGB to RGBA, because we might need to repaint with
// subpixel AA)
state.mRegionToInvalidate.And(state.mRegionToInvalidate, mVisibleRegion);
InheritContextFlags(target, state.mContext);
mXResolution = paintXRes;
mYResolution = paintYRes;
PaintBuffer(state.mContext,
@ -1016,7 +1005,6 @@ BasicLayerManager::PushGroupWithCachedSurface(gfxContext *aTarget,
mCachedSurface.Get(aContent,
gfxIntSize(clip.size.width, clip.size.height),
currentSurf);
InheritContextFlags(aTarget, ctx);
/* Align our buffer for the original surface */
ctx->Translate(-clip.pos);
*aSavedOffset = clip.pos;

View File

@ -46,22 +46,23 @@ using mozilla::MutexAutoLock;
ImageContainerD3D10::ImageContainerD3D10(LayerManagerD3D10 *aManager)
: ImageContainer(aManager)
, mDevice(aManager->device())
, mActiveImageLock("mozilla.layers.ImageContainerD3D10.mActiveImageLock")
{
}
already_AddRefed<Image>
ImageContainerD3D10::CreateImage(const Image::Format *aFormats,
PRUint32 aNumFormats)
PRUint32 aNumFormats)
{
if (!aNumFormats) {
return nsnull;
}
nsRefPtr<Image> img;
if (aFormats[0] == Image::PLANAR_YCBCR) {
img = new PlanarYCbCrImageD3D10(static_cast<LayerManagerD3D10*>(mManager));
img = new PlanarYCbCrImageD3D10(mDevice);
} else if (aFormats[0] == Image::CAIRO_SURFACE) {
img = new CairoImageD3D10(static_cast<LayerManagerD3D10*>(mManager));
img = new CairoImageD3D10(mDevice);
}
return img.forget();
}
@ -212,11 +213,11 @@ ImageLayerD3D10::RenderLayer(float aOpacity, const gfx3DMatrix &aTransform)
device()->Draw(4, 0);
}
PlanarYCbCrImageD3D10::PlanarYCbCrImageD3D10(mozilla::layers::LayerManagerD3D10* aManager)
PlanarYCbCrImageD3D10::PlanarYCbCrImageD3D10(ID3D10Device1 *aDevice)
: PlanarYCbCrImage(static_cast<ImageD3D10*>(this))
, mDevice(aDevice)
, mHasData(PR_FALSE)
{
mDevice = aManager->device();
}
void
@ -381,8 +382,8 @@ CairoImageD3D10::SetData(const CairoImage::Data &aData)
data.pSysMem = imageSurface->Data();
data.SysMemPitch = imageSurface->Stride();
mManager->device()->CreateTexture2D(&desc, &data, getter_AddRefs(mTexture));
mManager->device()->CreateShaderResourceView(mTexture, NULL, getter_AddRefs(mSRView));
mDevice->CreateTexture2D(&desc, &data, getter_AddRefs(mTexture));
mDevice->CreateShaderResourceView(mTexture, NULL, getter_AddRefs(mSRView));
}
already_AddRefed<gfxASurface>

View File

@ -69,6 +69,7 @@ private:
typedef mozilla::Mutex Mutex;
nsRefPtr<Image> mActiveImage;
nsRefPtr<ID3D10Device1> mDevice;
Mutex mActiveImageLock;
};
@ -100,7 +101,7 @@ class THEBES_API PlanarYCbCrImageD3D10 : public PlanarYCbCrImage,
public ImageD3D10
{
public:
PlanarYCbCrImageD3D10(LayerManagerD3D10 *aManager);
PlanarYCbCrImageD3D10(ID3D10Device1 *aDevice);
~PlanarYCbCrImageD3D10() {}
virtual void SetData(const Data &aData);
@ -134,9 +135,9 @@ class THEBES_API CairoImageD3D10 : public CairoImage,
public ImageD3D10
{
public:
CairoImageD3D10(LayerManagerD3D10 *aManager)
CairoImageD3D10(ID3D10Device1 *aDevice)
: CairoImage(static_cast<ImageD3D10*>(this))
, mManager(aManager)
, mDevice(aDevice)
{ }
~CairoImageD3D10();
@ -144,10 +145,10 @@ public:
virtual already_AddRefed<gfxASurface> GetAsSurface();
nsRefPtr<ID3D10Device1> mDevice;
nsRefPtr<ID3D10Texture2D> mTexture;
nsRefPtr<ID3D10ShaderResourceView> mSRView;
gfxIntSize mSize;
LayerManagerD3D10 *mManager;
};
} /* layers */

View File

@ -637,15 +637,7 @@ public:
* When this flag is set, snapping to device pixels is disabled.
* It simply never does anything.
*/
FLAG_DISABLE_SNAPPING = (1 << 1),
/**
* When this flag is set, rendering through this context
* is destined to be (eventually) drawn on the screen. It can be
* useful to know this, for example so that windowed plugins are
* not unnecessarily rendered (since they will already appear
* on the screen, thanks to their windows).
*/
FLAG_DESTINED_FOR_SCREEN = (1 << 2)
FLAG_DISABLE_SNAPPING = (1 << 1)
};
void SetFlag(PRInt32 aFlag) { mFlags |= aFlag; }

View File

@ -363,3 +363,16 @@ static const PRUint32 gCaseBlocks [8] = {
0x00000000,
0x80000000
};
// We map x -> x, except for upper-case letters,
// which we map to their lower-case equivalents.
static const PRUint8 gASCIIToLower [128] = {
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,
0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f,
0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f,
0x40, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f,
0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f,
0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f,
0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f,
};

View File

@ -281,6 +281,103 @@ static PRUnichar t4result[T4LEN+2] = {
0x0041 , // Dummy entry to prevent overflow
0x00
};
static unsigned char t6lhs[] = {
0x31 , // 0
0x19 , // 1
0x43 , // 2
0x67 , // 3
0xC3, 0x88 , // 4
0xC3, 0xA9 , // 5
0xC5, 0x87 , // 6
0xC7, 0x84 , // 7
0xC7, 0x86 , // 8
0xC7, 0x85 , // 9
0xCF, 0x80 , // 10
0xCE, 0xB2 , // 11
0xD0, 0xB8 , // 12
0xD2, 0xA5 , // 13
0xD7, 0x90 , // 14
0xE0, 0xA8, 0xA0 , // 15
0xE3, 0x82, 0xB0 , // 16
0xE5, 0x86, 0x85 , // 17
0xEC, 0x80, 0xA1 , // 18
0xEF, 0xBD, 0x88 , // 19
0xC7, 0x87 , // 20
0xC7, 0x88 , // 21
0xC7, 0x89 , // 22
0xC7, 0x8A , // 23
0xC7, 0x8B , // 24
0xC7, 0x8C , // 25
0xC7, 0xB1 , // 26
0xC7, 0xB2 , // 27
0xC7, 0xB3 , // 28
0xC9, 0x90 , // 29
0xC9, 0xB1 , // 30
0xEA, 0x99, 0x81 , // 31
0x00
};
static unsigned char t6rhs[] = {
0x31 , // 0
0x19 , // 1
0x43 , // 2
0x47 , // 3
0xC3, 0x88 , // 4
0xC3, 0x89 , // 5
0xC5, 0x87 , // 6
0xC7, 0x84 , // 7
0xC7, 0x84 , // 8
0xC7, 0x84 , // 9
0xCE, 0xA0 , // 10
0xCE, 0x92 , // 11
0xD0, 0x98 , // 12
0xD2, 0xA4 , // 13
0xD7, 0x90 , // 14
0xE0, 0xA8, 0xA0 , // 15
0xE3, 0x82, 0xB0 , // 16
0xE5, 0x86, 0x85 , // 17
0xEC, 0x80, 0xA1 , // 18
0xEF, 0xBC, 0xA8 , // 19
0xC7, 0x87 , // 20
0xC7, 0x87 , // 21
0xC7, 0x87 , // 22
0xC7, 0x8a , // 23
0xC7, 0x8a , // 24
0xC7, 0x8a , // 25
0xC7, 0xB1 , // 26
0xC7, 0xB1 , // 27
0xC7, 0xB1 , // 28
0xE2, 0xB1, 0xAF , // 29
0xE2, 0xB1, 0xAE , // 30
0xEA, 0x99, 0x80 , // 31
0x00
};
static const char *t7lhs = "aBcDeFGHIJKL1!!2!!a!uuuu";
static const char *t7rhs = "AbCdEFghijkL1!!2!!A!UUuU";
static const char *t8lhs = "aazzz";
static const char *t8rhs = "aBa";
static const char *t9lhs = "@a";
static const char *t9rhs = "`a";
bool CharByCharCompareEqual(const char *a, const char *b,
PRUint32 aLen, PRUint32 bLen)
{
// Do basically a CaseInsensitiveCompare(), but using
// CaseInsensitiveUTF8CharsEqual().
const char *aEnd = a + aLen;
const char *bEnd = b + bLen;
while (a < aEnd && b < bEnd) {
PRBool err;
if (!CaseInsensitiveUTF8CharsEqual(a, b, aEnd, bEnd, &a, &b, &err) || err)
return PR_FALSE;
}
return PR_TRUE;
}
void TestCaseConversion()
{
@ -291,7 +388,7 @@ void TestCaseConversion()
int i;
PRUnichar buf[256];
printf("Test 2 - ToUpper(PRUnichar, PRUnichar*):\n");
printf("Test 1 - ToUpper(PRUnichar, PRUnichar*):\n");
for(i=0;i < T2LEN ; i++)
{
PRUnichar ch = ToUpperCase(t2data[i]);
@ -300,7 +397,7 @@ void TestCaseConversion()
}
printf("Test 3 - ToLower(PRUnichar, PRUnichar*):\n");
printf("Test 2 - ToLower(PRUnichar, PRUnichar*):\n");
for(i=0;i < T3LEN; i++)
{
PRUnichar ch = ToLowerCase(t3data[i]);
@ -308,7 +405,7 @@ void TestCaseConversion()
printf("\tFailed!! result unexpected %d\n", i);
}
printf("Test 4 - ToTitle(PRUnichar, PRUnichar*):\n");
printf("Test 3 - ToTitle(PRUnichar, PRUnichar*):\n");
for(i=0;i < T4LEN; i++)
{
PRUnichar ch = ToTitleCase(t4data[i]);
@ -316,7 +413,7 @@ void TestCaseConversion()
printf("\tFailed!! result unexpected %d\n", i);
}
printf("Test 5 - ToUpper(PRUnichar*, PRUnichar*, PRUint32):\n");
printf("Test 4 - ToUpper(PRUnichar*, PRUnichar*, PRUint32):\n");
ToUpperCase(t2data, buf, T2LEN);
for(i = 0; i < T2LEN; i++)
{
@ -327,7 +424,7 @@ void TestCaseConversion()
}
}
printf("Test 6 - ToLower(PRUnichar*, PRUnichar*, PRUint32):\n");
printf("Test 5 - ToLower(PRUnichar*, PRUnichar*, PRUint32):\n");
ToLowerCase(t3data, buf, T3LEN);
for(i = 0; i < T3LEN; i++)
{
@ -338,11 +435,85 @@ void TestCaseConversion()
}
}
printf("Test 6 - CaseInsensitiveCompare UTF-8 (1):\n");
if (CaseInsensitiveCompare((char*)t6lhs, (char*)t6rhs, sizeof(t6lhs), sizeof(t6rhs)))
printf("\tFailed!\n");
if (!CharByCharCompareEqual((char*)t6lhs, (char*)t6rhs, sizeof(t6lhs), sizeof(t6rhs)))
printf("\tFailed character-by-character comparison!\n");
printf("Test 7 - CaseInsensitiveCompare UTF-8 (2):\n");
if (CaseInsensitiveCompare(t7lhs, t7rhs, strlen(t7lhs), strlen(t7rhs)))
printf("\tFailed!\n");
if (!CharByCharCompareEqual(t7lhs, t7rhs, sizeof(t7lhs), sizeof(t7rhs)))
printf("\tFailed character-by-character comparison!\n");
printf("Test 8a - CaseInsensitiveCompare UTF-8 (3):\n");
if (CaseInsensitiveCompare(t8lhs, t8rhs, strlen(t8lhs), strlen(t8rhs)) != -1)
printf("\tFailed!\n");
if (CharByCharCompareEqual(t8lhs, t8rhs, strlen(t8lhs), strlen(t8rhs)))
printf("\tFailed character-by-character comparison!\n");
printf("Test 8b - CaseInsensitiveCompare UTF-8 (4):\n");
if (CaseInsensitiveCompare(t8rhs, t8lhs, strlen(t8rhs), strlen(t8lhs)) != 1)
printf("\tFailed!\n");
// This test may seem a bit strange. But it's actually an easy bug to make
// if we tried to be clever and say that two ASCII characters x and y are
// case-insensitively equal if (x & ~0x20) == (y & ~0x20).
printf("Test 9 - CaseInsensitiveCompare UTF-8 (5):\n");
if (CaseInsensitiveCompare(t9rhs, t9lhs, strlen(t9lhs), strlen(t9rhs)) != 1)
printf("\tFailed!\n");
if (CharByCharCompareEqual(t9lhs, t9rhs, strlen(t9lhs), strlen(t9rhs)))
printf("\tFailed character-by-character comparison!\n");
printf("===========================\n");
printf("Finish case conversion test\n");
printf("===========================\n");
}
static void FuzzOneInvalidCaseConversion()
{
PRUint32 aLen = rand() % 32;
PRUint32 bLen = rand() % 32;
// We could use a static length-32 buffer for these, but then Valgrind
// wouldn't be able to detect errors.
unsigned char *aBuf = (unsigned char*)malloc(aLen * sizeof(unsigned char));
unsigned char *bBuf = (unsigned char*)malloc(bLen * sizeof(unsigned char));
for (PRUint32 i = 0; i < aLen; i++) {
aBuf[i] = rand() & 0xff;
}
for (PRUint32 i = 0; i < bLen; i++) {
bBuf[i] = rand() & 0xff;
}
CaseInsensitiveCompare((char*)aBuf, (char*)bBuf, aLen, bLen);
CharByCharCompareEqual((char*)aBuf, (char*)bBuf, aLen, bLen);
free(aBuf);
free(bBuf);
}
static void FuzzCaseConversion()
{
printf("==========================\n");
printf("Start fuzz case conversion\n");
printf("==========================\n");
srand(0);
printf("Fuzzing invalid UTF8 data...\n");
for (PRUint32 i = 0; i < 100000; i++) {
FuzzOneInvalidCaseConversion();
}
printf("===========================\n");
printf("Finish fuzz case conversion\n");
printf("===========================\n");
}
static void TestEntityConversion(PRUint32 version)
{
printf("==============================\n");
@ -567,6 +738,10 @@ int main(int argc, char** argv) {
// --------------------------------------------
FuzzCaseConversion();
// --------------------------------------------
TestEntityConversion(nsIEntityConverter::html40);
// --------------------------------------------

View File

@ -1,32 +1,3 @@
Instructions for using these tools are on the Mozilla Wiki.
* How to generate various properties files in intl/unicharutils/tables and
header files in intl/unicharutils/src
( written by Jungshik Shin for bug 210502
https://bugzilla.mozilla.org/show_bug.cgi?id=210502 on 2005-04-05 )
1. Grab the latest version of idnkit at http://www.nic.ad.jp/en/idn/index.html
(http://www.nic.ad.jp/ja/idn/idnkit/download/index.html )
2. There are three files we need in the kit:
generate_normalize_data.pl, UCD.pm and SparseMap.pm
3. a. Download the following Unicode data files :
CaseFolding.txt,CompositionExclusions.txt,
SpecialCasing.txt, UnicodeData.txt
b. Rename UnicodeData.txt to UnicodeData-Latest.txt
The latest version is, as of this writing, in
ftp://ftp.unicode.org/Public/4.1.0/ucd
4. a. Run generate_normalize_data.pl and save the output to a temporary file
b. Edit the file
- remove the case folding part (search for 'Lowercase' and delete
all the lines following it) because we have separate scripts for that,
- replace 'unsigned short' and 'unsigned long' with 'PRUnichar' and
'PRUint32'
c. Replace the actual source part (after the license) of
intl/unicharutil/src/normalization_data.h with the file you edited.
5. Generate casetable.h and cattable.h with gencasetable.pl and gencattable.pl
Just running them will put casetable.h and cattable.h in the right place.
https://wiki.mozilla.org/I18n:Updating_Unicode_version

View File

@ -383,6 +383,38 @@ for($idx=0;$idx<8;$idx++)
printf OUT "\n";
}
}
print OUT "};\n\n";
######################################################################
#
# Print out gASCIIToLower table
#
######################################################################
print OUT "// We map x -> x, except for upper-case letters,\n";
print OUT "// which we map to their lower-case equivalents.\n";
print OUT "static const PRUint8 gASCIIToLower [128] = {\n";
# Map x -> x, except for upper-case letters, which we map to lower-case
# letters.
for($idx=0; $idx < 128; $idx++)
{
if ($idx % 16 == 0) {
print OUT " "
}
if (65 <= $idx && $idx <= 90) {
printf OUT "0x%02x", ($idx + 0x20);
} else {
printf OUT "0x%02x", $idx;
}
if (($idx+1) % 16 != 0) {
print OUT ", ";
}
else {
print OUT ",\n";
}
}
print OUT "};\n";

View File

@ -46,10 +46,11 @@
#include "nsServiceManagerUtils.h"
#include "nsXPCOMStrings.h"
#include "casetable.h"
#include "nsUTF8Utils.h"
#include <ctype.h>
// For gUpperToTitle
// For gUpperToTitle
enum {
kUpperIdx =0,
kTitleIdx
@ -62,18 +63,19 @@ enum {
kDiffIdx
};
#define IS_ASCII(u) ( 0x0000 == ((u) & 0xFF80))
#define IS_ASCII_UPPER(u) ((0x0041 <= (u)) && ( (u) <= 0x005a))
#define IS_ASCII_LOWER(u) ((0x0061 <= (u)) && ( (u) <= 0x007a))
#define IS_ASCII(u) ((u) < 0x80)
#define IS_ASCII_UPPER(u) (('A' <= (u)) && ( (u) <= 'Z' ))
#define IS_ASCII_LOWER(u) (('a' <= (u)) && ( (u) <= 'z'))
#define IS_ASCII_ALPHA(u) (IS_ASCII_UPPER(u) || IS_ASCII_LOWER(u))
#define IS_ASCII_SPACE(u) ( 0x0020 == (u) )
#define IS_ASCII_SPACE(u) ( ' ' == (u) )
#define IS_NOCASE_CHAR(u) (0==(1&(gCaseBlocks[(u)>>13]>>(0x001F&((u)>>8)))))
// Size of Tables
#define CASE_MAP_CACHE_SIZE 0x40
#define CASE_MAP_CACHE_MASK 0x3F
// Changing these numbers may break UTF-8 caching. Be careful!
#define CASE_MAP_CACHE_SIZE 0x100
#define CASE_MAP_CACHE_MASK 0xFF
struct nsCompressedMap {
const PRUnichar *mTable;
@ -83,75 +85,94 @@ struct nsCompressedMap {
PRUnichar Map(PRUnichar aChar)
{
// no need to worry about thread safety since cached values are
// not objects but primitive data types which could be
// accessed in atomic operations. We need to access
// the whole 32 bit of cachedData at once in order to make it
// thread safe. Never access bits from mCache directly.
// We don't need explicit locking here since the cached values are int32s,
// which are read and written atomically. The following code is threadsafe
// because we never access bits from mCache directly -- we always first
// read the entire entry into a local variable and then mask off the bits
// we're interested in.
// Check the 256-byte cache first and bail with our answer if we can.
PRUint32 cachedData = mCache[aChar & CASE_MAP_CACHE_MASK];
if(aChar == ((cachedData >> 16) & 0x0000FFFF))
return (cachedData & 0x0000FFFF);
if (aChar == ((cachedData >> 16) & 0x0000FFFF))
return cachedData & 0x0000FFFF;
// try the last index first
// store into local variable so we can be thread safe
PRUint32 base = mLastBase;
// Now try the last index we looked up, storing it into a local variable
// for thread-safety.
PRUint32 base = mLastBase;
PRUnichar res = 0;
if (( aChar <= ((mTable[base+kSizeEveryIdx] >> 8) +
mTable[base+kLowIdx])) &&
( mTable[base+kLowIdx] <= aChar ))
{
// Hit the last base
if(((mTable[base+kSizeEveryIdx] & 0x00FF) > 0) &&
// Does this character fit in the slot?
if ((aChar <= ((mTable[base+kSizeEveryIdx] >> 8) +
mTable[base+kLowIdx])) &&
(mTable[base+kLowIdx] <= aChar)) {
// This character uses the same base as our last lookup, so the
// conversion is easy.
if (((mTable[base+kSizeEveryIdx] & 0x00FF) > 0) &&
(0 != ((aChar - mTable[base+kLowIdx]) %
(mTable[base+kSizeEveryIdx] & 0x00FF))))
{
res = aChar;
} else {
res = aChar + mTable[base+kDiffIdx];
}
(mTable[base+kSizeEveryIdx] & 0x00FF))))
{
res = aChar;
} else {
res = aChar + mTable[base+kDiffIdx];
}
} else {
res = this->Lookup(0, (mSize/2), mSize-1, aChar);
// Do the full lookup.
res = this->Lookup(0, mSize/2, mSize-1, aChar);
}
// Cache the result and return.
mCache[aChar & CASE_MAP_CACHE_MASK] =
(((aChar << 16) & 0xFFFF0000) | (0x0000FFFF & res));
((aChar << 16) & 0xFFFF0000) | (0x0000FFFF & res);
return res;
}
// Takes as arguments the left bound, middle, right bound, and character to
// search for. Executes a binary search.
PRUnichar Lookup(PRUint32 l,
PRUint32 m,
PRUint32 r,
PRUnichar aChar)
{
PRUint32 base = m*3;
if ( aChar > ((mTable[base+kSizeEveryIdx] >> 8) +
PRUint32 base = m*3; // Every line in the table is 3 units wide.
// Is aChar past the top of the current table entry? (The upper byte of
// the 'every' entry contains the offset to the end of this entry.)
if (aChar > ((mTable[base+kSizeEveryIdx] >> 8) +
mTable[base+kLowIdx]))
{
if( l > m )
if (l > m || l == r)
return aChar;
// Advance one round.
PRUint32 newm = (m+r+1)/2;
if(newm == m)
if (newm == m)
newm++;
return this->Lookup(m+1, newm , r, aChar);
} else if ( mTable[base+kLowIdx] > aChar ) {
if( r < m )
return this->Lookup(m+1, newm, r, aChar);
// Is aChar below the bottom of the current table entry?
} else if (mTable[base+kLowIdx] > aChar) {
if (r < m || l == r)
return aChar;
// Advance one round
PRUint32 newm = (l+m-1)/2;
if(newm == m)
newm++;
return this->Lookup(l, newm, m-1, aChar);
} else {
if(((mTable[base+kSizeEveryIdx] & 0x00FF) > 0) &&
(0 != ((aChar - mTable[base+kLowIdx]) %
(mTable[base+kSizeEveryIdx] & 0x00FF))))
// We've found the entry aChar should live in.
} else {
// Determine if aChar falls in a gap. (The lower byte of the 'every'
// entry contains n for which every nth character from the base is a
// character of interest.)
if (((mTable[base+kSizeEveryIdx] & 0x00FF) > 0) &&
(0 != ((aChar - mTable[base+kLowIdx]) %
(mTable[base+kSizeEveryIdx] & 0x00FF))))
{
return aChar;
}
mLastBase = base; // cache the base
// If aChar doesn't fall in the gap, cache and convert.
mLastBase = base;
return aChar + mTable[base+kDiffIdx];
}
}
@ -167,6 +188,29 @@ static nsCompressedMap gLowerMap = {
gToLowerItems
};
// We want ToLowerCase(PRUnichar) and ToLowerCaseASCII(PRUnichar) to be fast
// when they're called from within the case-insensitive comparators, so we
// define inlined versions.
static NS_ALWAYS_INLINE PRUnichar
ToLowerCase_inline(PRUnichar aChar)
{
if (IS_ASCII(aChar)) {
return gASCIIToLower[aChar];
} else if (IS_NOCASE_CHAR(aChar)) {
return aChar;
}
return gLowerMap.Map(aChar);
}
static NS_ALWAYS_INLINE PRUnichar
ToLowerCaseASCII_inline(const PRUnichar aChar)
{
if (IS_ASCII(aChar))
return gASCIIToLower[aChar];
return aChar;
}
void
ToLowerCase(nsAString& aString)
{
@ -189,9 +233,7 @@ ToLowerCase(const nsAString& aSource,
PRUnichar
ToLowerCaseASCII(const PRUnichar aChar)
{
if (IS_ASCII_UPPER(aChar))
return aChar + 0x0020;
return aChar;
return ToLowerCaseASCII_inline(aChar);
}
void
@ -218,115 +260,58 @@ ToUpperCase(const nsAString& aSource,
PRInt32
nsCaseInsensitiveStringComparator::operator()(const PRUnichar* lhs,
const PRUnichar* rhs,
PRUint32 aLength) const
PRUint32 lLength,
PRUint32 rLength) const
{
return CaseInsensitiveCompare(lhs, rhs, aLength);
return (lLength == rLength) ? CaseInsensitiveCompare(lhs, rhs, lLength) :
(lLength > rLength) ? 1 : -1;
}
PRInt32
nsCaseInsensitiveStringComparator::operator()(PRUnichar lhs,
PRUnichar rhs) const
nsCaseInsensitiveUTF8StringComparator::operator()(const char* lhs,
const char* rhs,
PRUint32 lLength,
PRUint32 rLength) const
{
// see if they're an exact match first
if (lhs == rhs)
return 0;
lhs = ToLowerCase(lhs);
rhs = ToLowerCase(rhs);
if (lhs == rhs)
return 0;
else if (lhs < rhs)
return -1;
else
return 1;
return CaseInsensitiveCompare(lhs, rhs, lLength, rLength);
}
PRInt32
nsASCIICaseInsensitiveStringComparator::operator()(const PRUnichar* lhs,
const PRUnichar* rhs,
PRUint32 aLength) const
PRUint32 lLength,
PRUint32 rLength) const
{
while (aLength) {
if (lLength != rLength) {
if (lLength > rLength)
return 1;
return -1;
}
while (rLength) {
PRUnichar l = *lhs++;
PRUnichar r = *rhs++;
if (l != r) {
l = ToLowerCaseASCII(l);
r = ToLowerCaseASCII(r);
l = ToLowerCaseASCII_inline(l);
r = ToLowerCaseASCII_inline(r);
if (l > r)
return 1;
else if (r > l)
return -1;
}
aLength--;
rLength--;
}
return 0;
}
PRInt32
nsASCIICaseInsensitiveStringComparator::operator()(PRUnichar lhs,
PRUnichar rhs) const
{
// see if they're an exact match first
if (lhs == rhs)
return 0;
lhs = ToLowerCaseASCII(lhs);
rhs = ToLowerCaseASCII(rhs);
if (lhs == rhs)
return 0;
else if (lhs < rhs)
return -1;
else
return 1;
}
#endif // MOZILLA_INTERNAL_API
PRInt32
CaseInsensitiveCompare(const PRUnichar *a,
const PRUnichar *b,
PRUint32 len)
{
NS_ASSERTION(a && b, "Do not pass in invalid pointers!");
if (len) {
do {
PRUnichar c1 = *a++;
PRUnichar c2 = *b++;
if (c1 != c2) {
c1 = ToLowerCase(c1);
c2 = ToLowerCase(c2);
if (c1 != c2) {
if (c1 < c2) {
return -1;
}
return 1;
}
}
} while (--len != 0);
}
return 0;
}
PRUnichar
ToLowerCase(PRUnichar aChar)
{
if (IS_ASCII(aChar)) {
if (IS_ASCII_UPPER(aChar))
return aChar + 0x0020;
else
return aChar;
} else if (IS_NOCASE_CHAR(aChar)) {
return aChar;
}
return gLowerMap.Map(aChar);
return ToLowerCase_inline(aChar);
}
void
@ -342,7 +327,7 @@ ToUpperCase(PRUnichar aChar)
{
if (IS_ASCII(aChar)) {
if (IS_ASCII_LOWER(aChar))
return aChar - 0x0020;
return aChar - 0x20;
else
return aChar;
} else if (IS_NOCASE_CHAR(aChar)) {
@ -380,7 +365,7 @@ ToTitleCase(PRUnichar aChar)
}
PRUnichar upper = gUpperMap.Map(aChar);
if (0x01C0 == ( upper & 0xFFC0)) {
for (PRUint32 i = 0 ; i < gUpperToTitleItems; i++) {
if (upper == gUpperToTitle[(i*2)+kUpperIdx]) {
@ -391,3 +376,167 @@ ToTitleCase(PRUnichar aChar)
return upper;
}
PRInt32
CaseInsensitiveCompare(const PRUnichar *a,
const PRUnichar *b,
PRUint32 len)
{
NS_ASSERTION(a && b, "Do not pass in invalid pointers!");
if (len) {
do {
PRUnichar c1 = *a++;
PRUnichar c2 = *b++;
if (c1 != c2) {
c1 = ToLowerCase_inline(c1);
c2 = ToLowerCase_inline(c2);
if (c1 != c2) {
if (c1 < c2) {
return -1;
}
return 1;
}
}
} while (--len != 0);
}
return 0;
}
// Calculates the codepoint of the UTF8 sequence starting at aStr. Sets aNext
// to the byte following the end of the sequence.
//
// If the sequence is invalid, or if computing the codepoint would take us off
// the end of the string (as marked by aEnd), returns -1 and does not set
// aNext. Note that this function doesn't check that aStr < aEnd -- it assumes
// you've done that already.
static NS_ALWAYS_INLINE PRUint32
GetLowerUTF8Codepoint(const char* aStr, const char* aEnd, const char **aNext)
{
// Convert to unsigned char so that stuffing chars into PRUint32s doesn't
// sign extend.
const unsigned char *str = (unsigned char*)aStr;
if (UTF8traits::isASCII(str[0])) {
// It's ASCII; just convert to lower-case and return it.
*aNext = aStr + 1;
return gASCIIToLower[*str];
}
if (UTF8traits::is2byte(str[0]) && NS_LIKELY(aStr + 1 < aEnd)) {
// It's a two-byte sequence, so it looks like
// 110XXXXX 10XXXXXX.
// This is definitely in the BMP, so we can store straightaway into a
// PRUint16.
PRUint16 c;
c = (str[0] & 0x1F) << 6;
c += (str[1] & 0x3F);
if (!IS_NOCASE_CHAR(c))
c = gLowerMap.Map(c);
*aNext = aStr + 2;
return c;
}
if (UTF8traits::is3byte(str[0]) && NS_LIKELY(aStr + 2 < aEnd)) {
// It's a three-byte sequence, so it looks like
// 1110XXXX 10XXXXXX 10XXXXXX.
// This will just barely fit into 16-bits, so store into a PRUint16.
PRUint16 c;
c = (str[0] & 0x0F) << 12;
c += (str[1] & 0x3F) << 6;
c += (str[2] & 0x3F);
if (!IS_NOCASE_CHAR(c))
c = gLowerMap.Map(c);
*aNext = aStr + 3;
return c;
}
if (UTF8traits::is4byte(str[0]) && NS_LIKELY(aStr + 3 < aEnd)) {
// It's a four-byte sequence, so it looks like
// 11110XXX 10XXXXXX 10XXXXXX 10XXXXXX.
// Unless this is an overlong sequence, the codepoint it encodes definitely
// isn't in the BMP, so we don't bother trying to convert it to lower-case.
PRUint32 c;
c = (str[0] & 0x07) << 18;
c += (str[1] & 0x3F) << 12;
c += (str[2] & 0x3F) << 6;
c += (str[3] & 0x3F);
*aNext = aStr + 4;
return c;
}
// Hm, we don't understand this sequence.
return -1;
}
PRInt32 CaseInsensitiveCompare(const char *aLeft,
const char *aRight,
PRUint32 aLeftBytes,
PRUint32 aRightBytes)
{
const char *leftEnd = aLeft + aLeftBytes;
const char *rightEnd = aRight + aRightBytes;
while (aLeft < leftEnd && aRight < rightEnd) {
PRUint32 leftChar = GetLowerUTF8Codepoint(aLeft, leftEnd, &aLeft);
if (NS_UNLIKELY(leftChar == PRUint32(-1)))
return -1;
PRUint32 rightChar = GetLowerUTF8Codepoint(aRight, rightEnd, &aRight);
if (NS_UNLIKELY(rightChar == PRUint32(-1)))
return -1;
// Now leftChar and rightChar are lower-case, so we can compare them.
if (leftChar != rightChar) {
if (leftChar > rightChar)
return 1;
return -1;
}
}
// Make sure that if one string is longer than the other we return the
// correct result.
if (aLeft < leftEnd)
return 1;
if (aRight < rightEnd)
return -1;
return 0;
}
PRBool
CaseInsensitiveUTF8CharsEqual(const char* aLeft, const char* aRight,
const char* aLeftEnd, const char* aRightEnd,
const char** aLeftNext, const char** aRightNext,
PRBool* aErr)
{
NS_ASSERTION(aLeftNext, "Out pointer shouldn't be null.");
NS_ASSERTION(aRightNext, "Out pointer shouldn't be null.");
NS_ASSERTION(aErr, "Out pointer shouldn't be null.");
NS_ASSERTION(aLeft < aLeftEnd, "aLeft must be less than aLeftEnd.");
NS_ASSERTION(aRight < aRightEnd, "aRight must be less than aRightEnd.");
PRUint32 leftChar = GetLowerUTF8Codepoint(aLeft, aLeftEnd, aLeftNext);
if (NS_UNLIKELY(leftChar == PRUint32(-1))) {
*aErr = PR_TRUE;
return PR_FALSE;
}
PRUint32 rightChar = GetLowerUTF8Codepoint(aRight, aRightEnd, aRightNext);
if (NS_UNLIKELY(rightChar == PRUint32(-1))) {
*aErr = PR_TRUE;
return PR_FALSE;
}
// Can't have an error past this point.
*aErr = PR_FALSE;
return leftChar == rightChar;
}

View File

@ -78,9 +78,17 @@ class nsCaseInsensitiveStringComparator : public nsStringComparator
public:
virtual PRInt32 operator() (const PRUnichar*,
const PRUnichar*,
PRUint32 aLength) const;
virtual PRInt32 operator() (PRUnichar,
PRUnichar) const;
PRUint32,
PRUint32) const;
};
class nsCaseInsensitiveUTF8StringComparator : public nsCStringComparator
{
public:
virtual PRInt32 operator() (const char*,
const char*,
PRUint32,
PRUint32) const;
};
class nsCaseInsensitiveStringArrayComparator
@ -97,9 +105,8 @@ class nsASCIICaseInsensitiveStringComparator : public nsStringComparator
public:
virtual int operator() (const PRUnichar*,
const PRUnichar*,
PRUint32 aLength) const;
virtual int operator() (PRUnichar,
PRUnichar) const;
PRUint32,
PRUint32) const;
};
inline PRBool
@ -126,4 +133,33 @@ CaseInsensitiveFindInReadable(const nsAString& aPattern,
PRInt32
CaseInsensitiveCompare(const PRUnichar *a, const PRUnichar *b, PRUint32 len);
PRInt32
CaseInsensitiveCompare(const char* aLeft, const char* aRight,
PRUint32 aLeftBytes, PRUint32 aRightBytes);
/**
* This function determines whether the UTF-8 sequence pointed to by aLeft is
* case-insensitively-equal to the UTF-8 sequence pointed to by aRight.
*
* aLeftEnd marks the first memory location past aLeft that is not part of
* aLeft; aRightEnd similarly marks the end of aRight.
*
* The function assumes that aLeft < aLeftEnd and aRight < aRightEnd.
*
* The function stores the addresses of the next characters in the sequence
* into aLeftNext and aRightNext. It's up to the caller to make sure that the
* returned pointers are valid -- i.e. the function may return aLeftNext >=
* aLeftEnd or aRightNext >= aRightEnd.
*
* If the function encounters invalid text, it sets aErr to true and returns
* false, possibly leaving aLeftNext and aRightNext uninitialized. If the
* function returns true, aErr is guaranteed to be false and both aLeftNext and
* aRightNext are guaranteed to be initialized.
*/
PRBool
CaseInsensitiveUTF8CharsEqual(const char* aLeft, const char* aRight,
const char* aLeftEnd, const char* aRightEnd,
const char** aLeftNext, const char** aRightNext,
PRBool* aErr);
#endif /* nsUnicharUtils_h__ */

View File

@ -595,8 +595,9 @@ bool Channel::ChannelImpl::ProcessOutgoingMessages() {
struct iovec iov = {const_cast<char*>(out_bytes), amt_to_write};
msgh.msg_iov = &iov;
msgh.msg_iovlen = 1;
char buf[CMSG_SPACE(
sizeof(int[FileDescriptorSet::MAX_DESCRIPTORS_PER_MESSAGE]))];
static const int tmp = CMSG_SPACE(sizeof(
int[FileDescriptorSet::MAX_DESCRIPTORS_PER_MESSAGE]));
char buf[tmp];
if (message_send_bytes_written_ == 0 &&
!msg->file_descriptor_set()->empty()) {

View File

@ -916,6 +916,8 @@ JS_StringToVersion(const char *string);
#define JSOPTION_METHODJIT JS_BIT(14) /* Whole-method JIT. */
#define JSOPTION_PROFILING JS_BIT(15) /* Profiler to make tracer/methodjit choices. */
#define JSOPTION_ROPES JS_BIT(16) /* Enable rope optimization for
* string concat. */
extern JS_PUBLIC_API(uint32)
JS_GetOptions(JSContext *cx);

View File

@ -108,8 +108,6 @@ JSString::flatten()
jschar *chars;
size_t capacity;
JS_ASSERT(isRope());
if (!isRope())
JS_CRASH(0xe0 | (mLengthAndFlags & TYPE_FLAGS_MASK));
/*
* This can be called from any string in the rope, so first traverse to the
@ -131,7 +129,7 @@ JSString::flatten()
* interior node with a NULL parent, so that we end up at NULL when we are
* done processing it.
*/
topNode->convertToInteriorNode((JSString *) 0x2);
topNode->convertToInteriorNode(NULL);
JSString *str = topNode, *next;
size_t pos = 0;
@ -139,7 +137,7 @@ JSString::flatten()
* Traverse the tree, making each interior string dependent on the resulting
* string.
*/
while (str != (JSString *) 0x2) {
while (str) {
switch (str->ropeTraversalCount()) {
case 0:
next = str->ropeLeft();
@ -270,6 +268,8 @@ FinishConcat(JSContext *cx, bool usingLeft, bool usingRight,
left->convertToInteriorNode(res);
if (usingRight)
right->convertToInteriorNode(res);
if (!JS_HAS_OPTION(cx, JSOPTION_ROPES))
res->flatten();
return res;
}
@ -300,9 +300,6 @@ js_ConcatStrings(JSContext *cx, JSString *left, JSString *right)
return shortStr->header();
}
left->checkCompartment(cx, 0xd0);
right->checkCompartment(cx, 0xd4);
/*
* We need to enforce a tree structure in ropes: every node needs to have a
* unique parent. So, we can't have the left or right child be in the middle

View File

@ -57,8 +57,6 @@
#include "jsvalue.h"
#include "jscell.h"
#define JS_CRASH(addr) *(int *) (addr) = 0;
#define JSSTRING_BIT(n) ((size_t)1 << (n))
#define JSSTRING_BITMASK(n) (JSSTRING_BIT(n) - 1)
@ -206,13 +204,6 @@ struct JSString {
return reinterpret_cast<js::gc::FreeCell *>(this);
}
inline void checkInteriorParent(int addr) {
if (isInteriorNode() && e.mParent == NULL)
JS_CRASH(addr);
}
inline void checkCompartment(JSContext *cx, int addr);
/*
* Generous but sane length bound; the "-1" is there for comptibility with
* OOM tests.
@ -287,7 +278,6 @@ struct JSString {
e.mCapacity = 0;
mLengthAndFlags = (length << FLAGS_LENGTH_SHIFT) | FLAT;
mChars = chars;
checkInteriorParent(0x90);
}
JS_ALWAYS_INLINE void initFlatMutable(jschar *chars, size_t length, size_t cap) {
@ -297,7 +287,6 @@ struct JSString {
e.mCapacity = cap;
mLengthAndFlags = (length << FLAGS_LENGTH_SHIFT) | FLAT | MUTABLE;
mChars = chars;
checkInteriorParent(0x94);
}
JS_ALWAYS_INLINE jschar *flatChars() const {
@ -346,14 +335,12 @@ struct JSString {
JS_ASSERT(isFlat());
JS_ASSERT(!isStatic(this));
JS_ATOMIC_SET_MASK((jsword *)&mLengthAndFlags, ATOMIZED);
checkInteriorParent(0x98);
}
inline void flatSetMutable() {
JS_ASSERT(isFlat());
JS_ASSERT(!isAtomized());
mLengthAndFlags |= MUTABLE;
checkInteriorParent(0x9c);
}
inline void flatClearMutable() {
@ -365,7 +352,6 @@ struct JSString {
*/
if (mLengthAndFlags & MUTABLE)
mLengthAndFlags &= ~MUTABLE;
checkInteriorParent(0xa0);
}
/*
@ -379,7 +365,6 @@ struct JSString {
mChars = chars;
mLengthAndFlags = DEPENDENT | (len << FLAGS_LENGTH_SHIFT);
e.mBase = bstr;
checkInteriorParent(0xa4);
}
inline JSString *dependentBase() const {
@ -405,16 +390,12 @@ struct JSString {
mLeft = left;
e.mRight = right;
e.mBufferWithInfo = buf;
checkInteriorParent(0xa8);
}
inline void convertToInteriorNode(JSString *parent) {
JS_ASSERT(isTopNode());
if (parent == NULL)
JS_CRASH(0x80);
e.mParent = parent;
mLengthAndFlags = INTERIOR_NODE | (length() << FLAGS_LENGTH_SHIFT);
checkInteriorParent(0xac);
}
inline JSString *interiorNodeParent() const {
@ -444,10 +425,7 @@ struct JSString {
inline void nullifyTopNodeBuffer() {
JS_ASSERT(isTopNode());
if (!isTopNode())
JS_CRASH(0x84);
e.mBufferWithInfo = NULL;
checkInteriorParent(0xb0);
}
/*
@ -466,13 +444,11 @@ struct JSString {
mLengthAndFlags = JSString::DEPENDENT |
((chars + end - mChars) << JSString::FLAGS_LENGTH_SHIFT);
e.mBase = base;
checkInteriorParent(0xb4);
}
inline void ropeClearTraversalCount() {
JS_ASSERT(isRope());
mLengthAndFlags &= ~ROPE_TRAVERSAL_COUNT_MASK;
checkInteriorParent(0xb8);
}
inline size_t ropeTraversalCount() const {
@ -484,7 +460,6 @@ struct JSString {
inline void ropeIncrementTraversalCount() {
JS_ASSERT(isRope());
mLengthAndFlags += ROPE_TRAVERSAL_COUNT_UNIT;
checkInteriorParent(0xbc);
}
inline bool ensureNotDependent(JSContext *cx) {

View File

@ -123,11 +123,4 @@ inline
JSRopeBuilder::JSRopeBuilder(JSContext *cx)
: cx(cx), mStr(cx->runtime->emptyString) {}
inline void
JSString::checkCompartment(JSContext *cx, int addr)
{
if (isRope() && asCell()->compartment() != cx->compartment)
JS_CRASH(addr);
}
#endif /* jsstrinlines_h___ */

View File

@ -5436,7 +5436,7 @@ main(int argc, char **argv, char **envp)
if (!cx)
return 1;
JS_SetOptions(cx, JS_GetOptions(cx) | JSOPTION_ANONFUNFIX);
JS_SetOptions(cx, JS_GetOptions(cx) | JSOPTION_ANONFUNFIX | JSOPTION_ROPES);
JS_SetGCParameterForThread(cx, JSGC_MAX_CODE_CACHE_BYTES, 16 * 1024 * 1024);
result = Shell(cx, argc, argv, envp);

View File

@ -740,13 +740,13 @@ public:
// we have not yet created the relevant frame.
PRPackedBool mHavePendingPopupgroup;
// If true (which is the default) then call SetPrimaryFrame() as needed
// during frame construction. If false, don't make any SetPrimaryFrame()
// calls. The mSetPrimaryFrames == PR_FALSE mode is meant to be used for
// If false (which is the default) then call SetPrimaryFrame() as needed
// during frame construction. If true, don't make any SetPrimaryFrame()
// calls. The mCreatingExtraFrames == PR_TRUE mode is meant to be used for
// construction of random "extra" frames for elements via normal frame
// construction APIs (e.g. replication of things across pages in paginated
// mode).
PRPackedBool mSetPrimaryFrames;
PRPackedBool mCreatingExtraFrames;
nsCOMArray<nsIContent> mGeneratedTextNodesWithInitializer;
@ -911,7 +911,7 @@ nsFrameConstructorState::nsFrameConstructorState(nsIPresShell* aPresShe
aAbsoluteContainingBlock->GetStyleDisplay()->
HasTransform()),
mHavePendingPopupgroup(PR_FALSE),
mSetPrimaryFrames(PR_TRUE),
mCreatingExtraFrames(PR_FALSE),
mCurrentPendingBindingInsertionPoint(&mPendingBindings)
{
#ifdef MOZ_XUL
@ -943,7 +943,7 @@ nsFrameConstructorState::nsFrameConstructorState(nsIPresShell* aPresShell,
aAbsoluteContainingBlock->GetStyleDisplay()->
HasTransform()),
mHavePendingPopupgroup(PR_FALSE),
mSetPrimaryFrames(PR_TRUE),
mCreatingExtraFrames(PR_FALSE),
mCurrentPendingBindingInsertionPoint(&mPendingBindings)
{
#ifdef MOZ_XUL
@ -2162,7 +2162,7 @@ NeedFrameFor(const nsFrameConstructorState& aState,
// XXX the GetContent() != aChildContent check is needed due to bug 135040.
// Remove it once that's fixed.
NS_PRECONDITION(!aChildContent->GetPrimaryFrame() ||
!aState.mSetPrimaryFrames ||
aState.mCreatingExtraFrames ||
aChildContent->GetPrimaryFrame()->GetContent() != aChildContent,
"Why did we get called?");
@ -3435,7 +3435,7 @@ nsCSSFrameConstructor::ConstructTextFrame(const FrameConstructionData* aData,
// Add the newly constructed frame to the flow
aFrameItems.AddChild(newFrame);
if (aState.mSetPrimaryFrames)
if (!aState.mCreatingExtraFrames)
aContent->SetPrimaryFrame(newFrame);
return rv;
@ -3710,6 +3710,13 @@ nsCSSFrameConstructor::ConstructFrameFromItemInternal(FrameConstructionItem& aIt
CHECK_ONLY_ONE_BIT(FCDATA_MAY_NEED_SCROLLFRAME, FCDATA_FORCE_VIEW);
#undef CHECK_ONLY_ONE_BIT
// Don't create a subdocument frame for iframes if we're creating extra frames
if (aState.mCreatingExtraFrames && aItem.mContent->IsHTML() &&
aItem.mContent->Tag() == nsGkAtoms::iframe)
{
return NS_OK;
}
nsStyleContext* const styleContext = aItem.mStyleContext;
const nsStyleDisplay* display = styleContext->GetStyleDisplay();
@ -3865,7 +3872,7 @@ nsCSSFrameConstructor::ConstructFrameFromItemInternal(FrameConstructionItem& aIt
((bits & FCDATA_IS_LINE_PARTICIPANT) != 0),
"Incorrectly set FCDATA_IS_LINE_PARTICIPANT bits");
if (aState.mSetPrimaryFrames && !(bits & FCDATA_SKIP_FRAMESET)) {
if (!aState.mCreatingExtraFrames && !(bits & FCDATA_SKIP_FRAMESET)) {
aItem.mContent->SetPrimaryFrame(primaryFrame);
}
@ -8407,7 +8414,7 @@ nsCSSFrameConstructor::CreateContinuingTableFrame(nsIPresShell* aPresShell,
nsFrameConstructorState state(mPresShell, mFixedContainingBlock,
GetAbsoluteContainingBlock(newFrame),
nsnull);
state.mSetPrimaryFrames = PR_FALSE;
state.mCreatingExtraFrames = PR_TRUE;
headerFooterFrame = static_cast<nsTableRowGroupFrame*>
(NS_NewTableRowGroupFrame(aPresShell, rowGroupFrame->GetStyleContext()));
@ -8734,7 +8741,7 @@ nsCSSFrameConstructor::ReplicateFixedFrames(nsPageContentFrame* aParentFrame)
nsFrameConstructorState state(mPresShell, aParentFrame,
nsnull,
mRootElementFrame);
state.mSetPrimaryFrames = PR_FALSE;
state.mCreatingExtraFrames = PR_TRUE;
// Iterate across fixed frames and replicate each whose placeholder is a
// descendant of aFrame. (We don't want to explicitly copy placeholders that
@ -11720,7 +11727,7 @@ PRBool
nsCSSFrameConstructor::
FrameConstructionItem::IsWhitespace(nsFrameConstructorState& aState) const
{
NS_PRECONDITION(!aState.mSetPrimaryFrames ||
NS_PRECONDITION(aState.mCreatingExtraFrames ||
!mContent->GetPrimaryFrame(), "How did that happen?");
if (!mIsText) {
return PR_FALSE;

View File

@ -2495,8 +2495,7 @@ PaintBackgroundLayer(nsPresContext* aPresContext,
}
}
if (aRenderingContext.ThebesContext()->GetFlags() &
gfxContext::FLAG_DESTINED_FOR_SCREEN) {
if (aFlags & nsCSSRendering::PAINTBG_TO_WINDOW) {
// Clip background-attachment:fixed backgrounds to the viewport, if we're
// painting to the screen. This avoids triggering tiling in common cases,
// without affecting output since drawing is always clipped to the viewport

View File

@ -224,8 +224,14 @@ struct nsCSSRendering {
*/
PAINTBG_WILL_PAINT_BORDER = 0x01,
/**
* When this flag is passed, images are synchronously decoded. */
PAINTBG_SYNC_DECODE_IMAGES = 0x02
* When this flag is passed, images are synchronously decoded.
*/
PAINTBG_SYNC_DECODE_IMAGES = 0x02,
/**
* When this flag is passed, painting will go to the screen so we can
* take advantage of the fact that it will be clipped to the viewport.
*/
PAINTBG_TO_WINDOW = 0x04
};
static void PaintBackground(nsPresContext* aPresContext,
nsIRenderingContext& aRenderingContext,

View File

@ -161,6 +161,9 @@ nsDisplayListBuilder::GetBackgroundPaintFlags() {
if (mSyncDecodeImages) {
flags |= nsCSSRendering::PAINTBG_SYNC_DECODE_IMAGES;
}
if (mIsPaintingToWindow) {
flags |= nsCSSRendering::PAINTBG_TO_WINDOW;
}
return flags;
}

View File

@ -734,7 +734,8 @@ public:
NS_IMETHOD SetDisplaySelection(PRInt16 aToggle);
NS_IMETHOD GetDisplaySelection(PRInt16 *aToggle);
NS_IMETHOD ScrollSelectionIntoView(SelectionType aType, SelectionRegion aRegion, PRBool aIsSynchronous);
NS_IMETHOD ScrollSelectionIntoView(SelectionType aType, SelectionRegion aRegion,
PRInt16 aFlags);
NS_IMETHOD RepaintSelection(SelectionType aType);
virtual NS_HIDDEN_(void) BeginObservingDocument();
@ -2574,12 +2575,13 @@ PresShell::GetCurrentSelection(SelectionType aType)
}
NS_IMETHODIMP
PresShell::ScrollSelectionIntoView(SelectionType aType, SelectionRegion aRegion, PRBool aIsSynchronous)
PresShell::ScrollSelectionIntoView(SelectionType aType, SelectionRegion aRegion,
PRInt16 aFlags)
{
if (!mSelection)
return NS_ERROR_NULL_POINTER;
return mSelection->ScrollSelectionIntoView(aType, aRegion, aIsSynchronous);
return mSelection->ScrollSelectionIntoView(aType, aRegion, aFlags);
}
NS_IMETHODIMP
@ -3129,7 +3131,9 @@ PresShell::PageMove(PRBool aForward, PRBool aExtend)
mSelection->CommonPageMove(aForward, aExtend, scrollableFrame);
// After ScrollSelectionIntoView(), the pending notifications might be
// flushed and PresShell/PresContext/Frames may be dead. See bug 418470.
return ScrollSelectionIntoView(nsISelectionController::SELECTION_NORMAL, nsISelectionController::SELECTION_FOCUS_REGION, PR_TRUE);
return ScrollSelectionIntoView(nsISelectionController::SELECTION_NORMAL,
nsISelectionController::SELECTION_FOCUS_REGION,
nsISelectionController::SCROLL_SYNCHRONOUS);
}
@ -3235,7 +3239,7 @@ PresShell::CompleteMove(PRBool aForward, PRBool aExtend)
// flushed and PresShell/PresContext/Frames may be dead. See bug 418470.
return ScrollSelectionIntoView(nsISelectionController::SELECTION_NORMAL,
nsISelectionController::SELECTION_FOCUS_REGION,
PR_TRUE);
nsISelectionController::SCROLL_SYNCHRONOUS);
}
NS_IMETHODIMP
@ -4300,7 +4304,7 @@ PresShell::ScrollFrameRectIntoView(nsIFrame* aFrame,
}
// only scroll one container when this flag is set
if (aFlags & SCROLL_FIRST_ANCESTOR_ONLY) {
if (aFlags & nsIPresShell::SCROLL_FIRST_ANCESTOR_ONLY) {
break;
}
@ -7177,7 +7181,8 @@ PresShell::PrepareToUseCaretPosition(nsIWidget* aEventWidget, nsIntPoint& aTarge
selCon = static_cast<nsISelectionController *>(this);
if (selCon) {
rv = selCon->ScrollSelectionIntoView(nsISelectionController::SELECTION_NORMAL,
nsISelectionController::SELECTION_FOCUS_REGION, PR_TRUE);
nsISelectionController::SELECTION_FOCUS_REGION,
nsISelectionController::SCROLL_SYNCHRONOUS);
NS_ENSURE_SUCCESS(rv, PR_FALSE);
}

View File

@ -218,6 +218,17 @@ function runTest4() {
function runTest4end() {
printpreview();
exitprintpreview();
runTest5();
}
// This is a crash test for bug 595337
function runTest5() {
window.frames[0].document.body.innerHTML =
'<iframe style="position: fixed; visibility: hidden; bottom: 10em;"></iframe>' +
'<input contenteditable="true" style="display: table; page-break-before: left; width: 10000px;">';
printpreview();
exitprintpreview();
SimpleTest.finish();
window.close();
}

View File

@ -655,7 +655,7 @@ nsTextControlFrame::ScrollOnFocusEvent::Run()
mFrame->mScrollEvent.Forget();
selCon->ScrollSelectionIntoView(nsISelectionController::SELECTION_NORMAL,
nsISelectionController::SELECTION_FOCUS_REGION,
PR_TRUE);
nsISelectionController::SCROLL_SYNCHRONOUS);
}
}
return NS_OK;
@ -856,7 +856,7 @@ nsTextControlFrame::SetSelectionInternal(nsIDOMNode *aStartNode,
// Scroll the selection into view (see bug 231389)
return selCon->ScrollSelectionIntoView(nsISelectionController::SELECTION_NORMAL,
nsISelectionController::SELECTION_FOCUS_REGION,
PR_FALSE);
nsISelectionController::SCROLL_FIRST_ANCESTOR_ONLY);
}
nsresult
@ -1370,11 +1370,11 @@ nsTextControlFrame::SetInitialChildList(nsIAtom* aListName,
// than descending from the root frame of the frame hierarchy.
if (first) {
first->AddStateBits(NS_FRAME_REFLOW_ROOT);
}
nsCOMPtr<nsITextControlElement> txtCtrl = do_QueryInterface(GetContent());
NS_ASSERTION(txtCtrl, "Content not a text control element");
txtCtrl->InitializeKeyboardEventListeners();
nsCOMPtr<nsITextControlElement> txtCtrl = do_QueryInterface(GetContent());
NS_ASSERTION(txtCtrl, "Content not a text control element");
txtCtrl->InitializeKeyboardEventListeners();
}
return rv;
}

View File

@ -388,13 +388,17 @@ public:
*
* @param aType the selection to scroll into view.
* @param aRegion the region inside the selection to scroll into view.
* @param aIsSynchronous when PR_TRUE, scrolls the selection into view
* at some point after the method returns.request which is processed
* @param aFlags the scroll flags. Valid bits include:
* SCROLL_SYNCHRONOUS: when set, scrolls the selection into view
* before returning. If not set, posts a request which is processed
* at some point after the method returns.
* SCROLL_FIRST_ANCESTOR_ONLY: if set, only the first ancestor will be scrolled
* into view.
*/
/*unsafe*/
nsresult ScrollSelectionIntoView(SelectionType aType,
SelectionRegion aRegion,
PRBool aIsSynchronous) const;
PRInt16 aFlags) const;
/** RepaintSelection repaints the selected frames that are inside the selection
* specified by aSelectionType.

View File

@ -1255,7 +1255,7 @@ nsDisplayPlugin::Paint(nsDisplayListBuilder* aBuilder,
nsIRenderingContext* aCtx)
{
nsObjectFrame* f = static_cast<nsObjectFrame*>(mFrame);
f->PaintPlugin(*aCtx, mVisibleRect, GetBounds(aBuilder));
f->PaintPlugin(aBuilder, *aCtx, mVisibleRect, GetBounds(aBuilder));
}
PRBool
@ -1779,7 +1779,8 @@ nsObjectFrame::BuildLayer(nsDisplayListBuilder* aBuilder,
}
void
nsObjectFrame::PaintPlugin(nsIRenderingContext& aRenderingContext,
nsObjectFrame::PaintPlugin(nsDisplayListBuilder* aBuilder,
nsIRenderingContext& aRenderingContext,
const nsRect& aDirtyRect, const nsRect& aPluginRect)
{
// Screen painting code
@ -2014,7 +2015,7 @@ nsObjectFrame::PaintPlugin(nsIRenderingContext& aRenderingContext,
nativeDraw.EndNativeDrawing();
} while (nativeDraw.ShouldRenderAgain());
nativeDraw.PaintToContext();
} else if (!(ctx->GetFlags() & gfxContext::FLAG_DESTINED_FOR_SCREEN)) {
} else if (!aBuilder->IsPaintingToWindow()) {
// Get PrintWindow dynamically since it's not present on Win2K,
// which we still support
typedef BOOL (WINAPI * PrintWindowPtr)
@ -2306,6 +2307,10 @@ nsObjectFrame::Instantiate(const char* aMimeType, nsIURI* aURI)
return NS_OK;
}
// XXXbz can aMimeType ever actually be null here? If not, either
// the callers are wrong (and passing "" instead of null) or we can
// remove the codepaths dealing with null aMimeType in
// InstantiateEmbeddedPlugin.
NS_ASSERTION(aMimeType || aURI, "Need a type or a URI!");
// Note: If PrepareInstanceOwner() returns an error, |this| may very

View File

@ -239,7 +239,8 @@ protected:
const nsRect& aDirtyRect, nsPoint aPt);
void PrintPlugin(nsIRenderingContext& aRenderingContext,
const nsRect& aDirtyRect);
void PaintPlugin(nsIRenderingContext& aRenderingContext,
void PaintPlugin(nsDisplayListBuilder* aBuilder,
nsIRenderingContext& aRenderingContext,
const nsRect& aDirtyRect, const nsRect& aPluginRect);
/**

View File

@ -200,13 +200,18 @@ public:
// to. The 'position' is a zero-width rectangle.
nsIFrame* GetSelectionAnchorGeometry(SelectionRegion aRegion, nsRect *aRect);
nsresult PostScrollSelectionIntoViewEvent(SelectionRegion aRegion);
nsresult PostScrollSelectionIntoViewEvent(SelectionRegion aRegion, PRBool aFirstAncestorOnly);
enum {
SCROLL_SYNCHRONOUS = 1<<1,
SCROLL_FIRST_ANCESTOR_ONLY = 1<<2,
SCROLL_DO_FLUSH = 1<<3
};
// aDoFlush only matters if aIsSynchronous is true. If not, we'll just flush
// when the scroll event fires so we make sure to scroll to the right place.
nsresult ScrollIntoView(SelectionRegion aRegion, PRBool aIsSynchronous,
PRBool aDoFlush,
nsresult ScrollIntoView(SelectionRegion aRegion,
PRInt16 aVPercent = NS_PRESSHELL_SCROLL_ANYWHERE,
PRInt16 aHPercent = NS_PRESSHELL_SCROLL_ANYWHERE);
PRInt16 aHPercent = NS_PRESSHELL_SCROLL_ANYWHERE,
PRInt32 aFlags = 0);
nsresult SubtractRange(RangeData* aRange, nsIRange* aSubtract,
nsTArray<RangeData>* aOutput);
nsresult AddItem(nsIRange *aRange, PRInt32* aOutIndex = nsnull);
@ -278,15 +283,18 @@ private:
public:
NS_DECL_NSIRUNNABLE
ScrollSelectionIntoViewEvent(nsTypedSelection *aTypedSelection,
SelectionRegion aRegion)
SelectionRegion aRegion,
PRBool aFirstAncestorOnly)
: mTypedSelection(aTypedSelection),
mRegion(aRegion) {
mRegion(aRegion),
mFirstAncestorOnly(aFirstAncestorOnly) {
NS_ASSERTION(aTypedSelection, "null parameter");
}
void Revoke() { mTypedSelection = nsnull; }
private:
nsTypedSelection *mTypedSelection;
SelectionRegion mRegion;
PRBool mFirstAncestorOnly;
};
void setAnchorFocusRange(PRInt32 aIndex); // pass in index into mRanges;
@ -459,15 +467,17 @@ public:
{
nsWeakFrame frame =
mContent ? mPresContext->GetPrimaryFrameFor(mContent) : nsnull;
if (!frame)
return NS_OK;
mContent = nsnull;
mFrameSelection->HandleDrag(frame, mPoint);
nsPoint pt = mPoint -
frame->GetOffsetTo(mPresContext->PresShell()->FrameManager()->GetRootFrame());
mFrameSelection->HandleDrag(frame, pt);
if (!frame.IsAlive())
return NS_OK;
NS_ASSERTION(frame->PresContext() == mPresContext, "document mismatch?");
nsPoint pt = mPoint -
frame->GetOffsetTo(mPresContext->PresShell()->FrameManager()->GetRootFrame());
mSelection->DoAutoScroll(frame, pt);
}
return NS_OK;
@ -1959,7 +1969,7 @@ nsFrameSelection::GetSelection(SelectionType aType) const
nsresult
nsFrameSelection::ScrollSelectionIntoView(SelectionType aType,
SelectionRegion aRegion,
PRBool aIsSynchronous) const
PRInt16 aFlags) const
{
PRInt8 index = GetIndexFromSelectionType(aType);
if (index < 0)
@ -1968,10 +1978,19 @@ nsFrameSelection::ScrollSelectionIntoView(SelectionType aType,
if (!mDomSelections[index])
return NS_ERROR_NULL_POINTER;
PRInt32 flags = nsTypedSelection::SCROLL_DO_FLUSH;
if (aFlags & nsISelectionController::SCROLL_SYNCHRONOUS) {
flags |= nsTypedSelection::SCROLL_SYNCHRONOUS;
} else if (aFlags & nsISelectionController::SCROLL_FIRST_ANCESTOR_ONLY) {
flags |= nsTypedSelection::SCROLL_FIRST_ANCESTOR_ONLY;
}
// After ScrollSelectionIntoView(), the pending notifications might be
// flushed and PresShell/PresContext/Frames may be dead. See bug 418470.
return mDomSelections[index]->ScrollIntoView(aRegion, aIsSynchronous,
PR_TRUE);
return mDomSelections[index]->ScrollIntoView(aRegion,
PRInt16(NS_PRESSHELL_SCROLL_ANYWHERE),
PRInt16(NS_PRESSHELL_SCROLL_ANYWHERE),
flags);
}
nsresult
@ -5550,13 +5569,22 @@ nsTypedSelection::ScrollSelectionIntoViewEvent::Run()
if (!mTypedSelection)
return NS_OK; // event revoked
PRInt32 flags = nsTypedSelection::SCROLL_DO_FLUSH |
nsTypedSelection::SCROLL_SYNCHRONOUS;
if (mFirstAncestorOnly) {
flags |= nsTypedSelection::SCROLL_FIRST_ANCESTOR_ONLY;
}
mTypedSelection->mScrollEvent.Forget();
mTypedSelection->ScrollIntoView(mRegion, PR_TRUE, PR_TRUE);
mTypedSelection->ScrollIntoView(mRegion,
PRInt16(NS_PRESSHELL_SCROLL_ANYWHERE),
PRInt16(NS_PRESSHELL_SCROLL_ANYWHERE),
flags);
return NS_OK;
}
nsresult
nsTypedSelection::PostScrollSelectionIntoViewEvent(SelectionRegion aRegion)
nsTypedSelection::PostScrollSelectionIntoViewEvent(SelectionRegion aRegion, PRBool aFirstAncestorOnly)
{
// If we've already posted an event, revoke it and place a new one at the
// end of the queue to make sure that any new pending reflow events are
@ -5565,7 +5593,7 @@ nsTypedSelection::PostScrollSelectionIntoViewEvent(SelectionRegion aRegion)
mScrollEvent.Revoke();
nsRefPtr<ScrollSelectionIntoViewEvent> ev =
new ScrollSelectionIntoViewEvent(this, aRegion);
new ScrollSelectionIntoViewEvent(this, aRegion, aFirstAncestorOnly);
nsresult rv = NS_DispatchToCurrentThread(ev);
NS_ENSURE_SUCCESS(rv, rv);
@ -5577,14 +5605,14 @@ NS_IMETHODIMP
nsTypedSelection::ScrollIntoView(SelectionRegion aRegion, PRBool aIsSynchronous,
PRInt16 aVPercent, PRInt16 aHPercent)
{
return ScrollIntoView(aRegion, aIsSynchronous, PR_FALSE,
aVPercent, aHPercent);
return ScrollIntoView(aRegion, aVPercent, aHPercent,
aIsSynchronous ? nsTypedSelection::SCROLL_SYNCHRONOUS : 0);
}
nsresult
nsTypedSelection::ScrollIntoView(SelectionRegion aRegion,
PRBool aIsSynchronous, PRBool aDoFlush,
PRInt16 aVPercent, PRInt16 aHPercent)
PRInt16 aVPercent, PRInt16 aHPercent,
PRInt32 aFlags)
{
nsresult result;
if (!mFrameSelection)
@ -5593,8 +5621,9 @@ nsTypedSelection::ScrollIntoView(SelectionRegion aRegion,
if (mFrameSelection->GetBatching())
return NS_OK;
if (!aIsSynchronous)
return PostScrollSelectionIntoViewEvent(aRegion);
if (!(aFlags & nsTypedSelection::SCROLL_SYNCHRONOUS))
return PostScrollSelectionIntoViewEvent(aRegion,
!!(aFlags & nsTypedSelection::SCROLL_FIRST_ANCESTOR_ONLY));
//
// Shut the caret off before scrolling to avoid
@ -5612,7 +5641,7 @@ nsTypedSelection::ScrollIntoView(SelectionRegion aRegion,
// is that some callers might scroll to the wrong place. Those should
// either manually flush if they're in a safe position for it or use the
// async version of this method.
if (aDoFlush) {
if (aFlags & nsTypedSelection::SCROLL_DO_FLUSH) {
presShell->FlushPendingNotifications(Flush_Layout);
// Reget the presshell, since it might have gone away.
@ -5632,7 +5661,8 @@ nsTypedSelection::ScrollIntoView(SelectionRegion aRegion,
if (!frame)
return NS_ERROR_FAILURE;
presShell->ScrollFrameRectIntoView(frame, rect, aVPercent, aHPercent, 0);
presShell->ScrollFrameRectIntoView(frame, rect, aVPercent, aHPercent,
(aFlags & nsTypedSelection::SCROLL_FIRST_ANCESTOR_ONLY) ? nsIPresShell::SCROLL_FIRST_ANCESTOR_ONLY: 0);
return NS_OK;
}
return result;

View File

@ -0,0 +1,13 @@
<html>
<head>
<link href="data:text/css,#master{display:table; width: 100%;}" rel="stylesheet" type="text/css" media="screen">
</head>
<body>
<div id="master">
<div style="height:10000px; background: -moz-linear-gradient(right bottom, rgb(0,0,0) 0%, rgb(255,255,255) 100%);">
</div>
<div style="height:100px"></div>
</div>
</html>

View File

@ -0,0 +1,14 @@
<html>
<head>
<link href="data:text/css,#master{display:table; width: 100%;}" rel="stylesheet" type="text/css" media="screen">
</head>
<body>
<div id="master">
<div style="height:10000px; background: -moz-linear-gradient(right bottom, rgb(0,0,0) 0%, rgb(255,255,255) 100%);">
</div>
<textarea style="height:100px; margin:0; border:0;"></textarea>
<script> </script>
</div>
</html>

View File

@ -1522,4 +1522,5 @@ fails-if(!haveTestPlugin) == 599476.html 599476-ref.html
== 604737.html 604737-ref.html
== 600974-2.html 600974-1-ref.html
== 600974-3.html 600974-1-ref.html
== 605138-1.html 605138-1-ref.html
== 605157-1.xhtml 605157-1-ref.xhtml

View File

@ -5,6 +5,8 @@
document.documentElement.pauseAnimations();
document.documentElement.setCurrentTime(1);
document.getElementById('a').setAttribute('end', '0s');
/* Force a sample to make sure that event gets fired */
document.documentElement.setCurrentTime(1);
delayedSnapshot(2)">
<script xlink:href="event-util.js" type="text/javascript"/>
<rect width="100" height="100" fill="red">

Before

Width:  |  Height:  |  Size: 612 B

After

Width:  |  Height:  |  Size: 728 B

View File

@ -12,7 +12,7 @@
== event-end-1.svg green-box-ref.svg
== event-end-2.svg green-box-ref.svg
== event-end-open-1.svg green-box-ref.svg
# == event-end-trimmed-1.svg green-box-ref.svg failing on mc, tracking in bug Bug 579828
== event-end-trimmed-1.svg green-box-ref.svg
== event-preventDefault-1.svg green-box-ref.svg
== event-seek-1.svg green-box-ref.svg
== event-target-default-1.svg green-box-ref.svg

View File

@ -668,18 +668,24 @@ canvas {
/* focusable content: anything w/ tabindex >=0 is focusable */
abbr:-moz-focusring, acronym:-moz-focusring, address:-moz-focusring,
applet:-moz-focusring,
b:-moz-focusring, base:-moz-focusring, big:-moz-focusring,
blockquote:-moz-focusring, br:-moz-focusring, canvas:-moz-focusring,
caption:-moz-focusring, center:-moz-focusring, cite:-moz-focusring,
code:-moz-focusring, col:-moz-focusring, colgroup:-moz-focusring,
dd:-moz-focusring, del:-moz-focusring, dfn:-moz-focusring, dir:-moz-focusring,
div:-moz-focusring, dl:-moz-focusring, dt:-moz-focusring, em:-moz-focusring,
embed:-moz-focusring,
fieldset:-moz-focusring, font:-moz-focusring, form:-moz-focusring,
h1:-moz-focusring, h2:-moz-focusring, h3:-moz-focusring, h4:-moz-focusring,
h5:-moz-focusring, h6:-moz-focusring, hr:-moz-focusring, i:-moz-focusring,
img:-moz-focusring, ins:-moz-focusring, kbd:-moz-focusring,
label:-moz-focusring, legend:-moz-focusring, li:-moz-focusring,
link:-moz-focusring, menu:-moz-focusring, ol:-moz-focusring, p:-moz-focusring,
link:-moz-focusring,
menu:-moz-focusring,
object:-moz-focusring,
ol:-moz-focusring,
p:-moz-focusring,
pre:-moz-focusring, q:-moz-focusring, s:-moz-focusring, samp:-moz-focusring,
small:-moz-focusring, span:-moz-focusring, strike:-moz-focusring,
strong:-moz-focusring, sub:-moz-focusring, sup:-moz-focusring,

View File

@ -557,45 +557,87 @@ nsStyleAnimation::ComputeDistance(nsCSSProperty aProperty,
return PR_TRUE;
}
case eUnit_Transform: {
const nsCSSValueList *list1 = aStartValue.GetCSSValueListValue();
const nsCSSValueList *list2 = aEndValue.GetCSSValueListValue();
nsStyleTransformMatrix matrix1, matrix2; // initialized to identity
PRBool dummy;
if (list1->mValue.GetUnit() != eCSSUnit_None) {
matrix1 = nsStyleTransformMatrix::ReadTransforms(list1, nsnull,
nsnull, dummy);
// Call AddWeighted to normalize to the format we use for
// interpolation. (This is far from ideal, but it provides good
// behavior for distance along a running transition.)
Value normValue1, normValue2;
if (!AddWeighted(aProperty, 1.0, aStartValue, 0.0, aEndValue,
normValue1) ||
!AddWeighted(aProperty, 0.0, aStartValue, 1.0, aEndValue,
normValue2)) {
return PR_FALSE;
}
if (list2->mValue.GetUnit() != eCSSUnit_None) {
matrix2 = nsStyleTransformMatrix::ReadTransforms(list2, nsnull,
nsnull, dummy);
const nsCSSValueList *list1 = normValue1.GetCSSValueListValue();
const nsCSSValueList *list2 = normValue2.GetCSSValueListValue();
NS_ABORT_IF_FALSE((list1->mValue.GetUnit() == eCSSUnit_None) ==
(list2->mValue.GetUnit() == eCSSUnit_None),
"none-ness should match after AddWeighted");
if (list1->mValue.GetUnit() == eCSSUnit_None) {
aDistance = 0;
return PR_TRUE;
}
double diff;
double squareDistance = 0.0;
for (PRUint32 i = 0; i < 4; ++i) {
diff = matrix1.GetMainMatrixEntry(i) - matrix2.GetMainMatrixEntry(i);
squareDistance += diff * diff;
for (; list1 && list2; list1 = list1->mNext, list2 = list2->mNext) {
NS_ABORT_IF_FALSE(list1->mValue.GetUnit() == eCSSUnit_Function &&
list2->mValue.GetUnit() == eCSSUnit_Function,
"unexpected unit");
const nsCSSValue::Array *a1 = list1->mValue.GetArrayValue(),
*a2 = list2->mValue.GetArrayValue();
NS_ABORT_IF_FALSE(a1->Item(0).GetUnit() == eCSSUnit_Ident &&
a2->Item(0).GetUnit() == eCSSUnit_Ident,
"unexpected unit");
NS_ABORT_IF_FALSE(a1->Item(0) == a2->Item(0),
"unexpected function mismatch");
nsCSSKeyword tfunc = nsStyleTransformMatrix::TransformFunctionOf(a1);
NS_ABORT_IF_FALSE(a1->Count() == a2->Count(),
"unexpected count mismatch");
for (size_t i = 1, iEnd = NS_MIN(a1->Count(), a2->Count());
i < iEnd; ++i) {
const nsCSSValue &v1 = a1->Item(i), &v2 = a2->Item(i);
NS_ABORT_IF_FALSE(v1.GetUnit() == eCSSUnit_Pixel ||
v1.GetUnit() == eCSSUnit_Percent ||
v1.GetUnit() == eCSSUnit_Calc ||
v1.GetUnit() == eCSSUnit_Radian ||
v1.GetUnit() == eCSSUnit_Number,
"unexpected unit");
NS_ABORT_IF_FALSE(v2.GetUnit() == eCSSUnit_Pixel ||
v2.GetUnit() == eCSSUnit_Percent ||
v2.GetUnit() == eCSSUnit_Calc ||
v2.GetUnit() == eCSSUnit_Radian ||
v2.GetUnit() == eCSSUnit_Number,
"unexpected unit");
if (v1.GetUnit() == eCSSUnit_Pixel ||
v1.GetUnit() == eCSSUnit_Percent ||
v1.GetUnit() == eCSSUnit_Calc) {
NS_ABORT_IF_FALSE(v2.GetUnit() == eCSSUnit_Pixel ||
v2.GetUnit() == eCSSUnit_Percent ||
v2.GetUnit() == eCSSUnit_Calc,
"unit mismatch");
CalcValue c1 = ExtractCalcValue(v1),
c2 = ExtractCalcValue(v2);
double diff = c1.mLength - c2.mLength;
squareDistance += diff * diff;
diff = c1.mPercent - c2.mPercent;
squareDistance += diff * diff;
} else {
NS_ABORT_IF_FALSE(v1.GetUnit() == v2.GetUnit(), "unit mismatch");
double diff;
if (tfunc == eCSSKeyword_skewx ||
tfunc == eCSSKeyword_skewy ||
tfunc == eCSSKeyword_skew) {
NS_ABORT_IF_FALSE(v1.GetUnit() == eCSSUnit_Radian, "unexpected unit");
diff = tan(v2.GetFloatValue()) - tan(v1.GetFloatValue());
} else {
diff = v2.GetFloatValue() - v1.GetFloatValue();
}
squareDistance += diff * diff;
}
}
}
diff = nsPresContext::AppUnitsToFloatCSSPixels(
matrix1.GetCoordXTranslation() - matrix2.GetCoordXTranslation());
squareDistance += diff * diff;
diff = nsPresContext::AppUnitsToFloatCSSPixels(
matrix1.GetCoordYTranslation() - matrix2.GetCoordYTranslation());
squareDistance += diff * diff;
diff = matrix1.GetWidthRelativeXTranslation() -
matrix2.GetWidthRelativeXTranslation();
squareDistance += diff * diff;
diff = matrix1.GetWidthRelativeYTranslation() -
matrix2.GetWidthRelativeYTranslation();
squareDistance += diff * diff;
diff = matrix1.GetHeightRelativeXTranslation() -
matrix2.GetHeightRelativeXTranslation();
squareDistance += diff * diff;
diff = matrix1.GetHeightRelativeYTranslation() -
matrix2.GetHeightRelativeYTranslation();
squareDistance += diff * diff;
NS_ABORT_IF_FALSE(!list1 && !list2,
"list lengths should match after AddWeighted");
aDistance = sqrt(squareDistance);
return PR_TRUE;
@ -707,36 +749,6 @@ AddCSSValuePercent(double aCoeff1, const nsCSSValue &aValue1,
aCoeff2 * aValue2.GetPercentValue());
}
// Add two non-canonical-form calc values (eUnit_Transform) to make
// another non-canonical-form calc value.
static void
AddCSSValueNoncanonicalCalc(double aCoeff1, const nsCSSValue &aValue1,
double aCoeff2, const nsCSSValue &aValue2,
nsCSSValue &aResult)
{
NS_ABORT_IF_FALSE(aValue1.GetUnit() == eCSSUnit_Percent ||
aValue1.GetUnit() == eCSSUnit_Pixel ||
aValue1.IsCalcUnit(), "unexpected unit");
NS_ABORT_IF_FALSE(aValue2.GetUnit() == eCSSUnit_Percent ||
aValue2.GetUnit() == eCSSUnit_Pixel ||
aValue2.IsCalcUnit(), "unexpected unit");
nsRefPtr<nsCSSValue::Array> a1 = nsCSSValue::Array::Create(2),
a2 = nsCSSValue::Array::Create(2),
atop = nsCSSValue::Array::Create(2),
acalc = nsCSSValue::Array::Create(1);
// Don't nest the eCSSUnit_Calc in our input inside any expressions.
a1->Item(0).SetFloatValue(aCoeff1, eCSSUnit_Number);
a1->Item(1) = aValue1.GetUnit() == eCSSUnit_Calc
? aValue1.GetArrayValue()->Item(0) : aValue1;
a2->Item(0).SetFloatValue(aCoeff2, eCSSUnit_Number);
a2->Item(1) = aValue2.GetUnit() == eCSSUnit_Calc
? aValue2.GetArrayValue()->Item(0) : aValue2;
atop->Item(0).SetArrayValue(a1, eCSSUnit_Calc_Times_L);
atop->Item(1).SetArrayValue(a2, eCSSUnit_Calc_Times_L);
acalc->Item(0).SetArrayValue(atop, eCSSUnit_Calc_Plus);
aResult.SetArrayValue(acalc, eCSSUnit_Calc);
}
// Add two canonical-form calc values (eUnit_Calc) to make another
// canonical-form calc value.
static void
@ -841,7 +853,7 @@ AddTransformTranslate(const nsCSSValue &aValue1, double aCoeff1,
if (aValue1.GetUnit() != aValue2.GetUnit() || aValue1.IsCalcUnit()) {
// different units; create a calc() expression
AddCSSValueNoncanonicalCalc(aCoeff1, aValue1, aCoeff2, aValue2, aResult);
AddCSSValueCanonicalCalc(aCoeff1, aValue1, aCoeff2, aValue2, aResult);
} else if (aValue1.GetUnit() == eCSSUnit_Percent) {
// both percent
AddCSSValuePercent(aCoeff1, aValue1, aCoeff2, aValue2, aResult);
@ -1108,7 +1120,7 @@ AddTransformMatrix(const nsStyleTransformMatrix &aMatrix1, double aCoeff1,
// append a rotate(90deg)
arr = AppendTransformFunction(eCSSKeyword_rotate, resultTail);
arr->Item(1).SetFloatValue(90.0f, eCSSUnit_Degree);
arr->Item(1).SetFloatValue(float(M_PI_2), eCSSUnit_Radian);
// append the translation for parts of the % translation components
// that were from inside a rotation
@ -1124,7 +1136,7 @@ AddTransformMatrix(const nsStyleTransformMatrix &aMatrix1, double aCoeff1,
// append a rotate(-90deg)
arr = AppendTransformFunction(eCSSKeyword_rotate, resultTail);
arr->Item(1).SetFloatValue(-90.0f, eCSSUnit_Degree);
arr->Item(1).SetFloatValue(-float(M_PI_2), eCSSUnit_Radian);
nscoord translateXCoord = NSToCoordRound(
aMatrix1.GetCoordXTranslation() * aCoeff1 +
@ -1905,8 +1917,12 @@ nsStyleAnimation::ComputeValue(nsCSSProperty aProperty,
"we should only be able to actively animate nodes that "
"are in a document");
nsCSSProperty propToParse =
nsCSSProps::PropHasFlags(aProperty, CSS_PROPERTY_REPORT_OTHER_NAME)
? nsCSSProps::OtherNameFor(aProperty) : aProperty;
nsRefPtr<nsStyleContext> tmpStyleContext =
StyleWithDeclarationAdded(aProperty, aTargetElement,
StyleWithDeclarationAdded(propToParse, aTargetElement,
aSpecifiedValue, aUseSVGMode);
if (!tmpStyleContext) {
return PR_FALSE;
@ -2116,13 +2132,25 @@ StyleCoordToCSSValue(const nsStyleCoord& aCoord, nsCSSValue& aCSSValue)
/*
* Assign |aOutput = aInput|, except with any non-pixel lengths
* replaced with the equivalent in pixels.
* replaced with the equivalent in pixels, and any non-canonical calc()
* expressions replaced with canonical ones.
*/
static void
SubstitutePixelValues(nsStyleContext* aStyleContext,
const nsCSSValue& aInput, nsCSSValue& aOutput)
{
if (aInput.UnitHasArrayValue()) {
if (aInput.IsCalcUnit()) {
PRBool canStoreInRuleTree = PR_TRUE;
nsRuleNode::ComputedCalc c =
nsRuleNode::SpecifiedCalcToComputedCalc(aInput, aStyleContext,
aStyleContext->PresContext(),
canStoreInRuleTree);
nsStyleCoord::Calc c2;
c2.mLength = c.mLength;
c2.mPercent = c.mPercent;
c2.mHasPercent = PR_TRUE; // doesn't matter for transform translate
SetCalcValue(&c2, aOutput);
} else if (aInput.UnitHasArrayValue()) {
const nsCSSValue::Array *inputArray = aInput.GetArrayValue();
nsRefPtr<nsCSSValue::Array> outputArray =
nsCSSValue::Array::Create(inputArray->Count());

Some files were not shown because too many files have changed in this diff Show More