зеркало из https://github.com/mozilla/gecko-dev.git
Merge m-c to fx-team. a=merge
This commit is contained in:
Коммит
b61dcafc3d
|
@ -7,6 +7,7 @@ let LoopUI;
|
|||
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "injectLoopAPI", "resource:///modules/loop/MozLoopAPI.jsm");
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "MozLoopService", "resource:///modules/loop/MozLoopService.jsm");
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "PanelFrame", "resource:///modules/PanelFrame.jsm");
|
||||
|
||||
|
||||
(function() {
|
||||
|
@ -19,41 +20,24 @@ XPCOMUtils.defineLazyModuleGetter(this, "MozLoopService", "resource:///modules/l
|
|||
* the panel to the button which triggers it.
|
||||
*/
|
||||
openCallPanel: function(event) {
|
||||
let panel = document.getElementById("loop-panel");
|
||||
let anchor = event.target;
|
||||
let iframe = document.getElementById("loop-panel-frame");
|
||||
let callback = iframe => {
|
||||
iframe.addEventListener("DOMContentLoaded", function documentDOMLoaded() {
|
||||
iframe.removeEventListener("DOMContentLoaded", documentDOMLoaded, true);
|
||||
injectLoopAPI(iframe.contentWindow);
|
||||
|
||||
if (!iframe) {
|
||||
// XXX This should be using SharedFrame (bug 1011392 may do this).
|
||||
iframe = document.createElement("iframe");
|
||||
iframe.setAttribute("id", "loop-panel-frame");
|
||||
iframe.setAttribute("type", "content");
|
||||
iframe.setAttribute("class", "loop-frame social-panel-frame");
|
||||
iframe.setAttribute("flex", "1");
|
||||
panel.appendChild(iframe);
|
||||
}
|
||||
// We use loopPanelInitialized so that we know we've finished localising before
|
||||
// sizing the panel.
|
||||
iframe.contentWindow.addEventListener("loopPanelInitialized",
|
||||
function documentLoaded() {
|
||||
iframe.contentWindow.removeEventListener("loopPanelInitialized",
|
||||
documentLoaded, true);
|
||||
}, true);
|
||||
|
||||
// We inject in DOMContentLoaded as that is before any scripts have tun.
|
||||
iframe.addEventListener("DOMContentLoaded", function documentDOMLoaded() {
|
||||
iframe.removeEventListener("DOMContentLoaded", documentDOMLoaded, true);
|
||||
injectLoopAPI(iframe.contentWindow);
|
||||
}, true);
|
||||
};
|
||||
|
||||
// We use loopPanelInitialized so that we know we've finished localising before
|
||||
// sizing the panel.
|
||||
iframe.contentWindow.addEventListener("loopPanelInitialized",
|
||||
function documentLoaded() {
|
||||
iframe.contentWindow.removeEventListener("loopPanelInitialized",
|
||||
documentLoaded, true);
|
||||
// XXX We end up with the wrong size here, so this
|
||||
// needs further investigation (bug 1011394).
|
||||
sizeSocialPanelToContent(panel, iframe);
|
||||
}, true);
|
||||
|
||||
}, true);
|
||||
|
||||
iframe.setAttribute("src", "about:looppanel");
|
||||
panel.hidden = false;
|
||||
panel.openPopup(anchor, "bottomcenter topright", 0, 0, false, false);
|
||||
PanelFrame.showPopup(window, PanelUI, event.target, "loop", null,
|
||||
"about:looppanel", null, callback);
|
||||
},
|
||||
|
||||
/**
|
||||
|
@ -63,6 +47,5 @@ XPCOMUtils.defineLazyModuleGetter(this, "MozLoopService", "resource:///modules/l
|
|||
initialize: function() {
|
||||
MozLoopService.initialize();
|
||||
},
|
||||
|
||||
};
|
||||
})();
|
||||
|
|
|
@ -12,13 +12,12 @@ let SocialUI,
|
|||
|
||||
(function() {
|
||||
|
||||
// The minimum sizes for the auto-resize panel code.
|
||||
const PANEL_MIN_HEIGHT = 100;
|
||||
const PANEL_MIN_WIDTH = 330;
|
||||
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "SharedFrame",
|
||||
"resource:///modules/SharedFrame.jsm");
|
||||
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "PanelFrame",
|
||||
"resource:///modules/PanelFrame.jsm");
|
||||
|
||||
XPCOMUtils.defineLazyGetter(this, "OpenGraphBuilder", function() {
|
||||
let tmp = {};
|
||||
Cu.import("resource:///modules/Social.jsm", tmp);
|
||||
|
@ -1145,66 +1144,6 @@ SocialStatus = {
|
|||
return this._toolbarHelper;
|
||||
},
|
||||
|
||||
get _dynamicResizer() {
|
||||
delete this._dynamicResizer;
|
||||
this._dynamicResizer = new DynamicResizeWatcher();
|
||||
return this._dynamicResizer;
|
||||
},
|
||||
|
||||
// status panels are one-per button per-process, we swap the docshells between
|
||||
// windows when necessary
|
||||
_attachNotificatonPanel: function(aParent, aButton, provider) {
|
||||
aParent.hidden = !SocialUI.enabled;
|
||||
let notificationFrameId = "social-status-" + provider.origin;
|
||||
let frame = document.getElementById(notificationFrameId);
|
||||
|
||||
// If the button was customized to a new location, we we'll destroy the
|
||||
// iframe and start fresh.
|
||||
if (frame && frame.parentNode != aParent) {
|
||||
SharedFrame.forgetGroup(frame.id);
|
||||
frame.parentNode.removeChild(frame);
|
||||
frame = null;
|
||||
}
|
||||
|
||||
if (!frame) {
|
||||
let size = provider.getPageSize("status");
|
||||
let {width, height} = size ? size : {width: PANEL_MIN_WIDTH, height: PANEL_MIN_HEIGHT};
|
||||
|
||||
frame = SharedFrame.createFrame(
|
||||
notificationFrameId, /* frame name */
|
||||
aParent, /* parent */
|
||||
{
|
||||
"type": "content",
|
||||
"mozbrowser": "true",
|
||||
"class": "social-panel-frame",
|
||||
"id": notificationFrameId,
|
||||
"tooltip": "aHTMLTooltip",
|
||||
"context": "contentAreaContextMenu",
|
||||
"flex": "1",
|
||||
|
||||
// work around bug 793057 - by making the panel roughly the final size
|
||||
// we are more likely to have the anchor in the correct position.
|
||||
"style": "width: " + width + "px; height: " + height + "px;",
|
||||
"dynamicresizer": !size,
|
||||
|
||||
"origin": provider.origin,
|
||||
"src": provider.statusURL
|
||||
}
|
||||
);
|
||||
|
||||
if (frame.socialErrorListener)
|
||||
frame.socialErrorListener.remove();
|
||||
if (frame.docShell) {
|
||||
frame.docShell.isActive = false;
|
||||
Social.setErrorListener(frame, this.setPanelErrorMessage.bind(this));
|
||||
}
|
||||
} else {
|
||||
frame.setAttribute("origin", provider.origin);
|
||||
SharedFrame.updateURL(notificationFrameId, provider.statusURL);
|
||||
}
|
||||
aButton.setAttribute("notificationFrameId", notificationFrameId);
|
||||
},
|
||||
|
||||
updateButton: function(origin) {
|
||||
let id = this._toolbarHelper.idFromOrigin(origin);
|
||||
let widget = CustomizableUI.getWidget(id);
|
||||
|
@ -1249,96 +1188,8 @@ SocialStatus = {
|
|||
let origin = aToolbarButton.getAttribute("origin");
|
||||
let provider = Social._getProviderFromOrigin(origin);
|
||||
|
||||
// if we're a slice in the hamburger, use that panel instead
|
||||
let widgetGroup = CustomizableUI.getWidget(aToolbarButton.getAttribute("id"));
|
||||
let widget = widgetGroup.forWindow(window);
|
||||
let panel, showingEvent, hidingEvent;
|
||||
let inMenuPanel = widgetGroup.areaType == CustomizableUI.TYPE_MENU_PANEL;
|
||||
if (inMenuPanel) {
|
||||
panel = document.getElementById("PanelUI-socialapi");
|
||||
this._attachNotificatonPanel(panel, aToolbarButton, provider);
|
||||
widget.node.setAttribute("closemenu", "none");
|
||||
showingEvent = "ViewShowing";
|
||||
hidingEvent = "ViewHiding";
|
||||
} else {
|
||||
panel = document.getElementById("social-notification-panel");
|
||||
this._attachNotificatonPanel(panel, aToolbarButton, provider);
|
||||
showingEvent = "popupshown";
|
||||
hidingEvent = "popuphidden";
|
||||
}
|
||||
let notificationFrameId = aToolbarButton.getAttribute("notificationFrameId");
|
||||
let notificationFrame = document.getElementById(notificationFrameId);
|
||||
|
||||
let wasAlive = SharedFrame.isGroupAlive(notificationFrameId);
|
||||
SharedFrame.setOwner(notificationFrameId, notificationFrame);
|
||||
|
||||
// Clear dimensions on all browsers so the panel size will
|
||||
// only use the selected browser.
|
||||
let frameIter = panel.firstElementChild;
|
||||
while (frameIter) {
|
||||
frameIter.collapsed = (frameIter != notificationFrame);
|
||||
frameIter = frameIter.nextElementSibling;
|
||||
}
|
||||
|
||||
function dispatchPanelEvent(name) {
|
||||
let evt = notificationFrame.contentDocument.createEvent("CustomEvent");
|
||||
evt.initCustomEvent(name, true, true, {});
|
||||
notificationFrame.contentDocument.documentElement.dispatchEvent(evt);
|
||||
}
|
||||
|
||||
// we only use a dynamic resizer when we're located the toolbar.
|
||||
let dynamicResizer;
|
||||
if (!inMenuPanel && notificationFrame.getAttribute("dynamicresizer") == "true") {
|
||||
dynamicResizer = this._dynamicResizer;
|
||||
}
|
||||
panel.addEventListener(hidingEvent, function onpopuphiding() {
|
||||
panel.removeEventListener(hidingEvent, onpopuphiding);
|
||||
aToolbarButton.removeAttribute("open");
|
||||
if (dynamicResizer)
|
||||
dynamicResizer.stop();
|
||||
notificationFrame.docShell.isActive = false;
|
||||
dispatchPanelEvent("socialFrameHide");
|
||||
});
|
||||
|
||||
panel.addEventListener(showingEvent, function onpopupshown() {
|
||||
panel.removeEventListener(showingEvent, onpopupshown);
|
||||
// This attribute is needed on both the button and the
|
||||
// containing toolbaritem since the buttons on OS X have
|
||||
// moz-appearance:none, while their container gets
|
||||
// moz-appearance:toolbarbutton due to the way that toolbar buttons
|
||||
// get combined on OS X.
|
||||
let initFrameShow = () => {
|
||||
notificationFrame.docShell.isActive = true;
|
||||
notificationFrame.docShell.isAppTab = true;
|
||||
if (dynamicResizer)
|
||||
dynamicResizer.start(panel, notificationFrame);
|
||||
dispatchPanelEvent("socialFrameShow");
|
||||
};
|
||||
if (!inMenuPanel)
|
||||
aToolbarButton.setAttribute("open", "true");
|
||||
if (notificationFrame.contentDocument &&
|
||||
notificationFrame.contentDocument.readyState == "complete" && wasAlive) {
|
||||
initFrameShow();
|
||||
} else {
|
||||
// first time load, wait for load and dispatch after load
|
||||
notificationFrame.addEventListener("load", function panelBrowserOnload(e) {
|
||||
notificationFrame.removeEventListener("load", panelBrowserOnload, true);
|
||||
initFrameShow();
|
||||
}, true);
|
||||
}
|
||||
});
|
||||
|
||||
if (inMenuPanel) {
|
||||
PanelUI.showSubView("PanelUI-socialapi", widget.node,
|
||||
CustomizableUI.AREA_PANEL);
|
||||
} else {
|
||||
let anchor = document.getAnonymousElementByAttribute(aToolbarButton, "class", "toolbarbutton-badge-container");
|
||||
// Bug 849216 - open the popup in a setTimeout so we avoid the auto-rollup
|
||||
// handling from preventing it being opened in some cases.
|
||||
setTimeout(function() {
|
||||
panel.openPopup(anchor, "bottomcenter topright", 0, 0, false, false);
|
||||
}, 0);
|
||||
}
|
||||
PanelFrame.showPopup(window, PanelUI, aToolbarButton, "social", origin,
|
||||
provider.statusURL, provider.getPageSize("status"));
|
||||
},
|
||||
|
||||
setPanelErrorMessage: function(aNotificationFrame) {
|
||||
|
|
|
@ -268,6 +268,12 @@
|
|||
position="topcenter topright"/>
|
||||
|
||||
#ifdef MOZ_LOOP
|
||||
<panel id="loop-notification-panel"
|
||||
class="loop-panel social-panel"
|
||||
type="arrow"
|
||||
hidden="true"
|
||||
noautofocus="true"/>
|
||||
|
||||
<panel id="loop-panel"
|
||||
class="loop-panel social-panel"
|
||||
type="arrow"
|
||||
|
@ -782,9 +788,13 @@
|
|||
|
||||
#ifdef MOZ_LOOP
|
||||
<!-- XXX Bug 1013989 will provide a label for the button -->
|
||||
<!-- This uses badged to be compatible with the social api code it shares.
|
||||
We may also want it to be badged in the future, for notification
|
||||
purposes. -->
|
||||
<toolbarbutton id="loop-call-button"
|
||||
class="toolbarbutton-1 chromeclass-toolbar-additional"
|
||||
persist="class"
|
||||
type="badged"
|
||||
removable="true"
|
||||
tooltiptext="&loopCallButton.tooltip;"
|
||||
oncommand="LoopUI.openCallPanel(event);"
|
||||
|
|
|
@ -141,6 +141,10 @@
|
|||
|
||||
<panelview id="PanelUI-socialapi" flex="1"/>
|
||||
|
||||
#ifdef MOZ_LOOP
|
||||
<panelview id="PanelUI-loopapi" flex="1"/>
|
||||
#endif
|
||||
|
||||
<panelview id="PanelUI-feeds" flex="1" oncommand="FeedHandler.subscribeToFeed(null, event);">
|
||||
<label value="&feedsMenu.label;" class="panel-subview-header"/>
|
||||
</panelview>
|
||||
|
|
|
@ -0,0 +1,209 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
* You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
"use strict";
|
||||
|
||||
// A module for working with panels with iframes shared across windows.
|
||||
|
||||
this.EXPORTED_SYMBOLS = ["PanelFrame"];
|
||||
|
||||
const { classes: Cc, interfaces: Ci, utils: Cu } = Components;
|
||||
|
||||
Cu.import("resource://gre/modules/Services.jsm");
|
||||
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "CustomizableUI", "resource:///modules/CustomizableUI.jsm");
|
||||
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "SharedFrame", "resource:///modules/SharedFrame.jsm");
|
||||
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "DynamicResizeWatcher", "resource:///modules/Social.jsm");
|
||||
|
||||
// The minimum sizes for the auto-resize panel code.
|
||||
const PANEL_MIN_HEIGHT = 100;
|
||||
const PANEL_MIN_WIDTH = 330;
|
||||
|
||||
let PanelFrameInternal = {
|
||||
/**
|
||||
* Helper function to get and hold a single instance of a DynamicResizeWatcher.
|
||||
*/
|
||||
get _dynamicResizer() {
|
||||
delete this._dynamicResizer;
|
||||
this._dynamicResizer = new DynamicResizeWatcher();
|
||||
return this._dynamicResizer;
|
||||
},
|
||||
|
||||
/**
|
||||
* Status panels are one-per button per-process, we swap the docshells between
|
||||
* windows when necessary.
|
||||
*
|
||||
* @param {DOMWindow} aWindow The window in which to show the popup.
|
||||
* @param {PanelUI} aPanelUI The panel UI object that represents the application menu.
|
||||
* @param {DOMElement} aButton The button element that is pressed to show the popup.
|
||||
* @param {String} aType The type of panel this is, e.g. "social" or "loop".
|
||||
* @param {String} aOrigin Optional, the origin to use for the iframe.
|
||||
* @param {String} aSrc The url to load into the iframe.
|
||||
* @param {String} aSize The initial size of the panel (width and height are the same
|
||||
* if specified).
|
||||
*/
|
||||
_attachNotificatonPanel: function(aWindow, aParent, aButton, aType, aOrigin, aSrc, aSize) {
|
||||
aParent.hidden = false;
|
||||
let notificationFrameId = aOrigin ? aType + "-status-" + aOrigin : aType;
|
||||
let frame = aWindow.document.getElementById(notificationFrameId);
|
||||
|
||||
// If the button was customized to a new location, we we'll destroy the
|
||||
// iframe and start fresh.
|
||||
if (frame && frame.parentNode != aParent) {
|
||||
SharedFrame.forgetGroup(frame.id);
|
||||
frame.parentNode.removeChild(frame);
|
||||
frame = null;
|
||||
}
|
||||
|
||||
if (!frame) {
|
||||
let {width, height} = aSize ? aSize : {width: PANEL_MIN_WIDTH, height: PANEL_MIN_HEIGHT};
|
||||
|
||||
frame = SharedFrame.createFrame(
|
||||
notificationFrameId, /* frame name */
|
||||
aParent, /* parent */
|
||||
{
|
||||
"type": "content",
|
||||
"mozbrowser": "true",
|
||||
// All frames use social-panel-frame as the class.
|
||||
"class": "social-panel-frame",
|
||||
"id": notificationFrameId,
|
||||
"tooltip": "aHTMLTooltip",
|
||||
"context": "contentAreaContextMenu",
|
||||
"flex": "1",
|
||||
|
||||
// work around bug 793057 - by making the panel roughly the final size
|
||||
// we are more likely to have the anchor in the correct position.
|
||||
"style": "width: " + width + "px; height: " + height + "px;",
|
||||
"dynamicresizer": !aSize,
|
||||
|
||||
"origin": aOrigin,
|
||||
"src": aSrc
|
||||
}
|
||||
);
|
||||
} else {
|
||||
frame.setAttribute("origin", aOrigin);
|
||||
SharedFrame.updateURL(notificationFrameId, aSrc);
|
||||
}
|
||||
aButton.setAttribute("notificationFrameId", notificationFrameId);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* The exported PanelFrame object
|
||||
*/
|
||||
let PanelFrame = {
|
||||
/**
|
||||
* Shows a popup in a pop-up panel, or in a sliding panel view in the application menu.
|
||||
* It will move the iframe to different DOM locations depending on where it needs to be
|
||||
* shown, enabling one iframe to be used for the entire session.
|
||||
*
|
||||
* @param {DOMWindow} aWindow The window in which to show the popup.
|
||||
* @param {PanelUI} aPanelUI The panel UI object that represents the application menu.
|
||||
* @param {DOMElement} aToolbarButton The button element that is pressed to show the popup.
|
||||
* @param {String} aType The type of panel this is, e.g. "social" or "loop".
|
||||
* @param {String} aOrigin Optional, the origin to use for the iframe.
|
||||
* @param {String} aSrc The url to load into the iframe.
|
||||
* @param {String} aSize The initial size of the panel (width and height are the same
|
||||
* if specified).
|
||||
* @param {Function} aCallback Optional, callback to be called with the iframe when it is
|
||||
* set up.
|
||||
*/
|
||||
showPopup: function(aWindow, aPanelUI, aToolbarButton, aType, aOrigin, aSrc, aSize, aCallback) {
|
||||
// if we're a slice in the hamburger, use that panel instead
|
||||
let widgetGroup = CustomizableUI.getWidget(aToolbarButton.getAttribute("id"));
|
||||
let widget = widgetGroup.forWindow(aWindow);
|
||||
let panel, showingEvent, hidingEvent;
|
||||
let inMenuPanel = widgetGroup.areaType == CustomizableUI.TYPE_MENU_PANEL;
|
||||
if (inMenuPanel) {
|
||||
panel = aWindow.document.getElementById("PanelUI-" + aType + "api");
|
||||
PanelFrameInternal._attachNotificatonPanel(aWindow, panel, aToolbarButton, aType, aOrigin, aSrc, aSize);
|
||||
widget.node.setAttribute("closemenu", "none");
|
||||
showingEvent = "ViewShowing";
|
||||
hidingEvent = "ViewHiding";
|
||||
} else {
|
||||
panel = aWindow.document.getElementById(aType + "-notification-panel");
|
||||
PanelFrameInternal._attachNotificatonPanel(aWindow, panel, aToolbarButton, aType, aOrigin, aSrc, aSize);
|
||||
showingEvent = "popupshown";
|
||||
hidingEvent = "popuphidden";
|
||||
}
|
||||
let notificationFrameId = aToolbarButton.getAttribute("notificationFrameId");
|
||||
let notificationFrame = aWindow.document.getElementById(notificationFrameId);
|
||||
|
||||
let wasAlive = SharedFrame.isGroupAlive(notificationFrameId);
|
||||
SharedFrame.setOwner(notificationFrameId, notificationFrame);
|
||||
|
||||
// Clear dimensions on all browsers so the panel size will
|
||||
// only use the selected browser.
|
||||
let frameIter = panel.firstElementChild;
|
||||
while (frameIter) {
|
||||
frameIter.collapsed = (frameIter != notificationFrame);
|
||||
frameIter = frameIter.nextElementSibling;
|
||||
}
|
||||
|
||||
function dispatchPanelEvent(name) {
|
||||
let evt = notificationFrame.contentDocument.createEvent("CustomEvent");
|
||||
evt.initCustomEvent(name, true, true, {});
|
||||
notificationFrame.contentDocument.documentElement.dispatchEvent(evt);
|
||||
}
|
||||
|
||||
// we only use a dynamic resizer when we're located the toolbar.
|
||||
let dynamicResizer;
|
||||
if (!inMenuPanel && notificationFrame.getAttribute("dynamicresizer") == "true") {
|
||||
dynamicResizer = PanelFrameInternal._dynamicResizer;
|
||||
}
|
||||
panel.addEventListener(hidingEvent, function onpopuphiding() {
|
||||
panel.removeEventListener(hidingEvent, onpopuphiding);
|
||||
aToolbarButton.removeAttribute("open");
|
||||
if (dynamicResizer)
|
||||
dynamicResizer.stop();
|
||||
notificationFrame.docShell.isActive = false;
|
||||
dispatchPanelEvent(aType + "FrameHide");
|
||||
});
|
||||
|
||||
panel.addEventListener(showingEvent, function onpopupshown() {
|
||||
panel.removeEventListener(showingEvent, onpopupshown);
|
||||
// This attribute is needed on both the button and the
|
||||
// containing toolbaritem since the buttons on OS X have
|
||||
// moz-appearance:none, while their container gets
|
||||
// moz-appearance:toolbarbutton due to the way that toolbar buttons
|
||||
// get combined on OS X.
|
||||
let initFrameShow = () => {
|
||||
notificationFrame.docShell.isActive = true;
|
||||
notificationFrame.docShell.isAppTab = true;
|
||||
if (dynamicResizer)
|
||||
dynamicResizer.start(panel, notificationFrame);
|
||||
dispatchPanelEvent(aType + "FrameShow");
|
||||
};
|
||||
if (!inMenuPanel)
|
||||
aToolbarButton.setAttribute("open", "true");
|
||||
if (notificationFrame.contentDocument &&
|
||||
notificationFrame.contentDocument.readyState == "complete" && wasAlive) {
|
||||
initFrameShow();
|
||||
} else {
|
||||
// first time load, wait for load and dispatch after load
|
||||
notificationFrame.addEventListener("load", function panelBrowserOnload(e) {
|
||||
notificationFrame.removeEventListener("load", panelBrowserOnload, true);
|
||||
initFrameShow();
|
||||
}, true);
|
||||
}
|
||||
});
|
||||
|
||||
if (inMenuPanel) {
|
||||
aPanelUI.showSubView("PanelUI-" + aType + "api", widget.node,
|
||||
CustomizableUI.AREA_PANEL);
|
||||
} else {
|
||||
let anchor = aWindow.document.getAnonymousElementByAttribute(aToolbarButton, "class", "toolbarbutton-badge-container");
|
||||
// Bug 849216 - open the popup asynchronously so we avoid the auto-rollup
|
||||
// handling from preventing it being opened in some cases.
|
||||
Services.tm.mainThread.dispatch(function() {
|
||||
panel.openPopup(anchor, "bottomcenter topright", 0, 0, false, false);
|
||||
}, Ci.nsIThread.DISPATCH_NORMAL);
|
||||
}
|
||||
|
||||
if (aCallback)
|
||||
aCallback(notificationFrame);
|
||||
}
|
||||
};
|
|
@ -17,6 +17,7 @@ EXTRA_JS_MODULES += [
|
|||
'Feeds.jsm',
|
||||
'NetworkPrioritizer.jsm',
|
||||
'offlineAppCache.jsm',
|
||||
'PanelFrame.jsm',
|
||||
'RemotePrompt.jsm',
|
||||
'SharedFrame.jsm',
|
||||
'SitePermissions.jsm',
|
||||
|
|
|
@ -510,6 +510,9 @@ class Automation(object):
|
|||
else:
|
||||
env['MOZ_CRASHREPORTER_DISABLE'] = '1'
|
||||
|
||||
# Crash on non-local network connections.
|
||||
env['MOZ_DISABLE_NONLOCAL_CONNECTIONS'] = '1'
|
||||
|
||||
env['GNOME_DISABLE_CRASH_DIALOG'] = '1'
|
||||
env['XRE_NO_WINDOWS_CRASH_DIALOG'] = '1'
|
||||
env['NS_TRACE_MALLOC_DISABLE_STACKS'] = '1'
|
||||
|
|
|
@ -494,6 +494,9 @@ def environment(xrePath, env=None, crashreporter=True, debugger=False, dmdPath=N
|
|||
else:
|
||||
env['MOZ_CRASHREPORTER_DISABLE'] = '1'
|
||||
|
||||
# Crash on non-local network connections.
|
||||
env['MOZ_DISABLE_NONLOCAL_CONNECTIONS'] = '1'
|
||||
|
||||
# Set WebRTC logging in case it is not set yet
|
||||
env.setdefault('NSPR_LOG_MODULES', 'signaling:5,mtransport:5,datachannel:5')
|
||||
env.setdefault('R_LOG_LEVEL', '6')
|
||||
|
|
|
@ -70,6 +70,9 @@ class RemoteAutomation(Automation):
|
|||
else:
|
||||
env['MOZ_CRASHREPORTER_DISABLE'] = '1'
|
||||
|
||||
# Crash on non-local network connections.
|
||||
env['MOZ_DISABLE_NONLOCAL_CONNECTIONS'] = '1'
|
||||
|
||||
return env
|
||||
|
||||
def waitForFinish(self, proc, utilityPath, timeout, maxTime, startTime, debuggerInfo, symbolsPath):
|
||||
|
|
|
@ -2068,6 +2068,7 @@ GK_ATOM(overlay_scrollbars, "overlay-scrollbars")
|
|||
GK_ATOM(windows_default_theme, "windows-default-theme")
|
||||
GK_ATOM(mac_graphite_theme, "mac-graphite-theme")
|
||||
GK_ATOM(mac_lion_theme, "mac-lion-theme")
|
||||
GK_ATOM(mac_yosemite_theme, "mac-yosemite-theme")
|
||||
GK_ATOM(windows_compositor, "windows-compositor")
|
||||
GK_ATOM(windows_glass, "windows-glass")
|
||||
GK_ATOM(touch_enabled, "touch-enabled")
|
||||
|
@ -2099,6 +2100,7 @@ GK_ATOM(_moz_overlay_scrollbars, "-moz-overlay-scrollbars")
|
|||
GK_ATOM(_moz_windows_default_theme, "-moz-windows-default-theme")
|
||||
GK_ATOM(_moz_mac_graphite_theme, "-moz-mac-graphite-theme")
|
||||
GK_ATOM(_moz_mac_lion_theme, "-moz-mac-lion-theme")
|
||||
GK_ATOM(_moz_mac_yosemite_theme, "-moz-mac-yosemite-theme")
|
||||
GK_ATOM(_moz_windows_compositor, "-moz-windows-compositor")
|
||||
GK_ATOM(_moz_windows_classic, "-moz-windows-classic")
|
||||
GK_ATOM(_moz_windows_glass, "-moz-windows-glass")
|
||||
|
|
|
@ -360,7 +360,6 @@ skip-if = buildapp == 'b2g' # b2g(bug 901343, specialpowers.wrap issue [nsIChann
|
|||
[test_bug422403-2.xhtml]
|
||||
skip-if = buildapp == 'b2g'
|
||||
[test_bug422537.html]
|
||||
skip-if = buildapp == 'b2g' || e10s # b2g(xmlhttprequest causes crash, bug 902271) b2g-debug(xmlhttprequest causes crash, bug 902271) b2g-desktop(xmlhttprequest causes crash, bug 902271)
|
||||
[test_bug424212.html]
|
||||
[test_bug424359-1.html]
|
||||
skip-if = buildapp == 'b2g'
|
||||
|
@ -375,7 +374,6 @@ skip-if = (buildapp == 'b2g' && toolkit != 'gonk') #Bug 931116, b2g desktop spec
|
|||
[test_bug429157.html]
|
||||
[test_bug431082.html]
|
||||
[test_bug431701.html]
|
||||
skip-if = buildapp == 'b2g' || e10s # b2g(xmlhttprequest causes crash, bug 902271) b2g-debug(xmlhttprequest causes crash, bug 902271) b2g-desktop(xmlhttprequest causes crash, bug 902271)
|
||||
[test_bug431833.html]
|
||||
[test_bug433533.html]
|
||||
[test_bug433662.html]
|
||||
|
|
|
@ -0,0 +1,42 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
const Cc = Components.classes;
|
||||
const Ci = Components.interfaces;
|
||||
const Cu = Components.utils;
|
||||
|
||||
Cu.import("resource://testing-common/httpd.js");
|
||||
Cu.import("resource://gre/modules/NetUtil.jsm");
|
||||
|
||||
var server = new HttpServer();
|
||||
server.start(-1);
|
||||
|
||||
var docbody = '<html xmlns="http://www.w3.org/1999/xhtml"><head></head><body></body></html>';
|
||||
|
||||
function handler(metadata, response) {
|
||||
let body = NetUtil.readInputStreamToString(metadata.bodyInputStream,
|
||||
metadata.bodyInputStream.available());
|
||||
response.setStatusLine(metadata.httpVersion, 200, "OK");
|
||||
response.write(body, body.length);
|
||||
}
|
||||
|
||||
function run_test() {
|
||||
do_test_pending();
|
||||
server.registerPathHandler("/foo", handler);
|
||||
|
||||
var parser = Cc["@mozilla.org/xmlextras/domparser;1"].createInstance(Ci.nsIDOMParser);
|
||||
parser.init();
|
||||
let doc = parser.parseFromString(docbody, "text/html");
|
||||
let xhr = Cc['@mozilla.org/xmlextras/xmlhttprequest;1'].createInstance(Ci.nsIXMLHttpRequest);
|
||||
xhr.onload = function() {
|
||||
do_check_eq(xhr.responseText, docbody);
|
||||
server.stop(do_test_finished);
|
||||
};
|
||||
xhr.onerror = function() {
|
||||
do_check_false(false);
|
||||
server.stop(do_test_finished);
|
||||
};
|
||||
xhr.open("POST", "http://localhost:" + server.identity.primaryPort + "/foo", true);
|
||||
xhr.send(doc);
|
||||
}
|
|
@ -30,6 +30,7 @@ run-sequentially = Hardcoded 4444 port.
|
|||
# Bug 1018414: hardcoded localhost doesn't work properly on some OS X installs
|
||||
skip-if = os == 'mac'
|
||||
[test_thirdpartyutil.js]
|
||||
[test_xhr_document.js]
|
||||
[test_xhr_standalone.js]
|
||||
[test_xmlserializer.js]
|
||||
[test_csp_ignores_path.js]
|
||||
|
|
|
@ -0,0 +1,3 @@
|
|||
function run_test() {
|
||||
run_test_in_child("../unit/test_xhr_document.js");
|
||||
}
|
|
@ -3,3 +3,4 @@ head =
|
|||
tail =
|
||||
|
||||
[test_bug553888_wrap.js]
|
||||
[test_xhr_document_ipc.js]
|
||||
|
|
|
@ -4358,7 +4358,7 @@ CanvasPath::CanvasPath(nsISupports* aParent)
|
|||
mPathBuilder = gfxPlatform::GetPlatform()->ScreenReferenceDrawTarget()->CreatePathBuilder();
|
||||
}
|
||||
|
||||
CanvasPath::CanvasPath(nsISupports* aParent, RefPtr<PathBuilder> aPathBuilder)
|
||||
CanvasPath::CanvasPath(nsISupports* aParent, TemporaryRef<PathBuilder> aPathBuilder)
|
||||
: mParent(aParent), mPathBuilder(aPathBuilder)
|
||||
{
|
||||
SetIsDOMBinding();
|
||||
|
@ -4555,8 +4555,8 @@ CanvasPath::BezierTo(const gfx::Point& aCP1,
|
|||
mPathBuilder->BezierTo(aCP1, aCP2, aCP3);
|
||||
}
|
||||
|
||||
RefPtr<gfx::Path>
|
||||
CanvasPath::GetPath(const CanvasWindingRule& winding, const mozilla::RefPtr<mozilla::gfx::DrawTarget>& mTarget) const
|
||||
TemporaryRef<gfx::Path>
|
||||
CanvasPath::GetPath(const CanvasWindingRule& winding, const DrawTarget* aTarget) const
|
||||
{
|
||||
FillRule fillRule = FillRule::FILL_WINDING;
|
||||
if (winding == CanvasWindingRule::Evenodd) {
|
||||
|
@ -4564,7 +4564,7 @@ CanvasPath::GetPath(const CanvasWindingRule& winding, const mozilla::RefPtr<mozi
|
|||
}
|
||||
|
||||
if (mPath &&
|
||||
(mPath->GetBackendType() == mTarget->GetType()) &&
|
||||
(mPath->GetBackendType() == aTarget->GetType()) &&
|
||||
(mPath->GetFillRule() == fillRule)) {
|
||||
return mPath;
|
||||
}
|
||||
|
@ -4580,8 +4580,8 @@ CanvasPath::GetPath(const CanvasWindingRule& winding, const mozilla::RefPtr<mozi
|
|||
}
|
||||
|
||||
// retarget our backend if we're used with a different backend
|
||||
if (mPath->GetBackendType() != mTarget->GetType()) {
|
||||
RefPtr<PathBuilder> tmpPathBuilder = mTarget->CreatePathBuilder(fillRule);
|
||||
if (mPath->GetBackendType() != aTarget->GetType()) {
|
||||
RefPtr<PathBuilder> tmpPathBuilder = aTarget->CreatePathBuilder(fillRule);
|
||||
mPath->StreamToSink(tmpPathBuilder);
|
||||
mPath = tmpPathBuilder->Finish();
|
||||
} else if (mPath->GetFillRule() != fillRule) {
|
||||
|
|
|
@ -85,11 +85,14 @@ public:
|
|||
const gfx::Point& aCP2,
|
||||
const gfx::Point& aCP3);
|
||||
|
||||
mozilla::RefPtr<mozilla::gfx::Path> GetPath(const CanvasWindingRule& winding,
|
||||
const mozilla::RefPtr<mozilla::gfx::DrawTarget>& mTarget) const;
|
||||
TemporaryRef<gfx::Path> GetPath(const CanvasWindingRule& aWinding,
|
||||
const gfx::DrawTarget* aTarget) const;
|
||||
|
||||
explicit CanvasPath(nsISupports* aParent);
|
||||
CanvasPath(nsISupports* aParent, RefPtr<gfx::PathBuilder> mPathBuilder);
|
||||
// TemporaryRef arg because the return value from Path::CopyToBuilder() is
|
||||
// passed directly and we can't drop the only ref to have a raw pointer.
|
||||
CanvasPath(nsISupports* aParent,
|
||||
TemporaryRef<gfx::PathBuilder> aPathBuilder);
|
||||
virtual ~CanvasPath() {}
|
||||
|
||||
private:
|
||||
|
|
|
@ -21,7 +21,7 @@ namespace dom {
|
|||
class HTMLFormElement;
|
||||
class HTMLImageElement;
|
||||
class OwningNodeListOrElement;
|
||||
template<typename> class Nullable;
|
||||
template<typename> struct Nullable;
|
||||
|
||||
class HTMLFormControlsCollection : public nsIHTMLCollection
|
||||
, public nsWrapperCache
|
||||
|
|
|
@ -27,7 +27,7 @@ class ErrorResult;
|
|||
namespace dom {
|
||||
|
||||
class OwningNodeOrHTMLCollection;
|
||||
template<typename> class Nullable;
|
||||
template<typename> struct Nullable;
|
||||
|
||||
class HTMLAllCollection MOZ_FINAL : public nsISupports
|
||||
, public nsWrapperCache
|
||||
|
|
|
@ -15,7 +15,7 @@
|
|||
#include "nsIThread.h"
|
||||
|
||||
class nsIFile;
|
||||
template <class> class already_AddRefed;
|
||||
template <class> struct already_AddRefed;
|
||||
|
||||
namespace mozilla {
|
||||
namespace gmp {
|
||||
|
|
|
@ -58,8 +58,13 @@ function log(msg) {
|
|||
|
||||
function finish(v) {
|
||||
log("finish: " + v.name);
|
||||
v.parentNode.removeChild(v);
|
||||
clearInterval(v.checkStateInterval);
|
||||
|
||||
for (var i=0; i<gEventTypes.length; i++) {
|
||||
v.removeEventListener(gEventTypes[i], listener, false);
|
||||
}
|
||||
removeNodeAndSource(v);
|
||||
|
||||
manager.finished(v.token);
|
||||
v = null;
|
||||
}
|
||||
|
@ -86,6 +91,7 @@ function createMedia(type, src, token) {
|
|||
for (var i=0; i<gEventTypes.length; i++) {
|
||||
v.addEventListener(gEventTypes[i], listener, false);
|
||||
}
|
||||
v.preload = "metadata";
|
||||
v.src = src;
|
||||
v.name = src;
|
||||
document.body.appendChild(v);
|
||||
|
|
|
@ -588,7 +588,7 @@ SVGContentUtils::CoordToFloat(nsPresContext *aPresContext,
|
|||
}
|
||||
}
|
||||
|
||||
RefPtr<gfx::Path>
|
||||
TemporaryRef<gfx::Path>
|
||||
SVGContentUtils::GetPath(const nsAString& aPathString)
|
||||
{
|
||||
SVGPathData pathData;
|
||||
|
|
|
@ -249,7 +249,7 @@ public:
|
|||
* Returns a path
|
||||
* string formatted as an SVG path
|
||||
*/
|
||||
static mozilla::RefPtr<mozilla::gfx::Path>
|
||||
static mozilla::TemporaryRef<mozilla::gfx::Path>
|
||||
GetPath(const nsAString& aPathString);
|
||||
};
|
||||
|
||||
|
|
|
@ -14,7 +14,7 @@
|
|||
|
||||
class nsIStackFrame;
|
||||
template <class T>
|
||||
class already_AddRefed;
|
||||
struct already_AddRefed;
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
|
|
@ -25,6 +25,7 @@
|
|||
|
||||
class DeviceStorageFile;
|
||||
class nsIInputStream;
|
||||
class nsIOutputStream;
|
||||
|
||||
namespace mozilla {
|
||||
class EventListenerManager;
|
||||
|
@ -92,6 +93,9 @@ public:
|
|||
nsresult Remove();
|
||||
nsresult Write(nsIInputStream* aInputStream);
|
||||
nsresult Write(InfallibleTArray<uint8_t>& bits);
|
||||
nsresult Append(nsIInputStream* aInputStream);
|
||||
nsresult Append(nsIInputStream* aInputStream,
|
||||
nsIOutputStream* aOutputStream);
|
||||
void CollectFiles(nsTArray<nsRefPtr<DeviceStorageFile> >& aFiles,
|
||||
PRTime aSince = 0);
|
||||
void collectFilesInternal(nsTArray<nsRefPtr<DeviceStorageFile> >& aFiles,
|
||||
|
@ -218,6 +222,13 @@ public:
|
|||
already_AddRefed<DOMRequest>
|
||||
AddNamed(nsIDOMBlob* aBlob, const nsAString& aPath, ErrorResult& aRv);
|
||||
|
||||
already_AddRefed<DOMRequest>
|
||||
AppendNamed(nsIDOMBlob* aBlob, const nsAString& aPath, ErrorResult& aRv);
|
||||
|
||||
already_AddRefed<DOMRequest>
|
||||
AddOrAppendNamed(nsIDOMBlob* aBlob, const nsAString& aPath,
|
||||
const int32_t aRequestType, ErrorResult& aRv);
|
||||
|
||||
already_AddRefed<DOMRequest>
|
||||
Get(const nsAString& aPath, ErrorResult& aRv)
|
||||
{
|
||||
|
|
|
@ -49,7 +49,31 @@ DeviceStorageRequestParent::Dispatch()
|
|||
nsCOMPtr<nsIInputStream> stream;
|
||||
blob->GetInternalStream(getter_AddRefs(stream));
|
||||
|
||||
nsRefPtr<CancelableRunnable> r = new WriteFileEvent(this, dsf, stream);
|
||||
nsRefPtr<CancelableRunnable> r = new WriteFileEvent(this, dsf, stream,
|
||||
DEVICE_STORAGE_REQUEST_CREATE);
|
||||
|
||||
nsCOMPtr<nsIEventTarget> target
|
||||
= do_GetService(NS_STREAMTRANSPORTSERVICE_CONTRACTID);
|
||||
MOZ_ASSERT(target);
|
||||
target->Dispatch(r, NS_DISPATCH_NORMAL);
|
||||
break;
|
||||
}
|
||||
|
||||
case DeviceStorageParams::TDeviceStorageAppendParams:
|
||||
{
|
||||
DeviceStorageAppendParams p = mParams;
|
||||
|
||||
nsRefPtr<DeviceStorageFile> dsf =
|
||||
new DeviceStorageFile(p.type(), p.storageName(), p.relpath());
|
||||
|
||||
BlobParent* bp = static_cast<BlobParent*>(p.blobParent());
|
||||
nsCOMPtr<nsIDOMBlob> blob = bp->GetBlob();
|
||||
|
||||
nsCOMPtr<nsIInputStream> stream;
|
||||
blob->GetInternalStream(getter_AddRefs(stream));
|
||||
|
||||
nsRefPtr<CancelableRunnable> r = new WriteFileEvent(this, dsf, stream,
|
||||
DEVICE_STORAGE_REQUEST_APPEND);
|
||||
|
||||
nsCOMPtr<nsIEventTarget> target
|
||||
= do_GetService(NS_STREAMTRANSPORTSERVICE_CONTRACTID);
|
||||
|
@ -244,6 +268,14 @@ DeviceStorageRequestParent::EnsureRequiredPermissions(
|
|||
break;
|
||||
}
|
||||
|
||||
case DeviceStorageParams::TDeviceStorageAppendParams:
|
||||
{
|
||||
DeviceStorageAppendParams p = mParams;
|
||||
type = p.type();
|
||||
requestType = DEVICE_STORAGE_REQUEST_APPEND;
|
||||
break;
|
||||
}
|
||||
|
||||
case DeviceStorageParams::TDeviceStorageCreateFdParams:
|
||||
{
|
||||
DeviceStorageCreateFdParams p = mParams;
|
||||
|
@ -576,10 +608,12 @@ DeviceStorageRequestParent::CreateFdEvent::CancelableRun()
|
|||
DeviceStorageRequestParent::WriteFileEvent::
|
||||
WriteFileEvent(DeviceStorageRequestParent* aParent,
|
||||
DeviceStorageFile* aFile,
|
||||
nsIInputStream* aInputStream)
|
||||
nsIInputStream* aInputStream,
|
||||
int32_t aRequestType)
|
||||
: CancelableRunnable(aParent)
|
||||
, mFile(aFile)
|
||||
, mInputStream(aInputStream)
|
||||
, mRequestType(aRequestType)
|
||||
{
|
||||
}
|
||||
|
||||
|
@ -600,14 +634,26 @@ DeviceStorageRequestParent::WriteFileEvent::CancelableRun()
|
|||
}
|
||||
|
||||
bool check = false;
|
||||
nsresult rv;
|
||||
mFile->mFile->Exists(&check);
|
||||
if (check) {
|
||||
r = new PostErrorEvent(mParent, POST_ERROR_EVENT_FILE_EXISTS);
|
||||
|
||||
if (mRequestType == DEVICE_STORAGE_REQUEST_CREATE) {
|
||||
if (check) {
|
||||
r = new PostErrorEvent(mParent, POST_ERROR_EVENT_FILE_EXISTS);
|
||||
return NS_DispatchToMainThread(r);
|
||||
}
|
||||
rv = mFile->Write(mInputStream);
|
||||
} else if (mRequestType == DEVICE_STORAGE_REQUEST_APPEND) {
|
||||
if (!check) {
|
||||
r = new PostErrorEvent(mParent, POST_ERROR_EVENT_FILE_DOES_NOT_EXIST);
|
||||
return NS_DispatchToMainThread(r);
|
||||
}
|
||||
rv = mFile->Append(mInputStream);
|
||||
} else {
|
||||
r = new PostErrorEvent(mParent, POST_ERROR_EVENT_UNKNOWN);
|
||||
return NS_DispatchToMainThread(r);
|
||||
}
|
||||
|
||||
nsresult rv = mFile->Write(mInputStream);
|
||||
|
||||
if (NS_FAILED(rv)) {
|
||||
r = new PostErrorEvent(mParent, POST_ERROR_EVENT_UNKNOWN);
|
||||
}
|
||||
|
|
|
@ -132,12 +132,14 @@ private:
|
|||
class WriteFileEvent : public CancelableRunnable
|
||||
{
|
||||
public:
|
||||
WriteFileEvent(DeviceStorageRequestParent* aParent, DeviceStorageFile* aFile, nsIInputStream* aInputStream);
|
||||
WriteFileEvent(DeviceStorageRequestParent* aParent, DeviceStorageFile* aFile,
|
||||
nsIInputStream* aInputStream, int32_t aRequestType);
|
||||
virtual ~WriteFileEvent();
|
||||
virtual nsresult CancelableRun();
|
||||
private:
|
||||
nsRefPtr<DeviceStorageFile> mFile;
|
||||
nsCOMPtr<nsIInputStream> mInputStream;
|
||||
int32_t mRequestType;
|
||||
};
|
||||
|
||||
class DeleteFileEvent : public CancelableRunnable
|
||||
|
|
|
@ -408,6 +408,7 @@ DeviceStorageTypeChecker::GetAccessForRequest(
|
|||
aAccessResult.AssignLiteral("read");
|
||||
break;
|
||||
case DEVICE_STORAGE_REQUEST_WRITE:
|
||||
case DEVICE_STORAGE_REQUEST_APPEND:
|
||||
case DEVICE_STORAGE_REQUEST_DELETE:
|
||||
case DEVICE_STORAGE_REQUEST_FORMAT:
|
||||
case DEVICE_STORAGE_REQUEST_MOUNT:
|
||||
|
@ -1063,9 +1064,6 @@ DeviceStorageFile::Write(nsIInputStream* aInputStream)
|
|||
return rv;
|
||||
}
|
||||
|
||||
uint64_t bufSize = 0;
|
||||
aInputStream->Available(&bufSize);
|
||||
|
||||
nsCOMPtr<nsIOutputStream> outputStream;
|
||||
NS_NewLocalFileOutputStream(getter_AddRefs(outputStream), mFile);
|
||||
|
||||
|
@ -1073,36 +1071,7 @@ DeviceStorageFile::Write(nsIInputStream* aInputStream)
|
|||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIOutputStream> bufferedOutputStream;
|
||||
rv = NS_NewBufferedOutputStream(getter_AddRefs(bufferedOutputStream),
|
||||
outputStream,
|
||||
4096*4);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
while (bufSize) {
|
||||
uint32_t wrote;
|
||||
rv = bufferedOutputStream->WriteFrom(
|
||||
aInputStream,
|
||||
static_cast<uint32_t>(std::min<uint64_t>(bufSize, UINT32_MAX)),
|
||||
&wrote);
|
||||
if (NS_FAILED(rv)) {
|
||||
break;
|
||||
}
|
||||
bufSize -= wrote;
|
||||
}
|
||||
|
||||
iocomplete = new IOEventComplete(this, "modified");
|
||||
rv = NS_DispatchToMainThread(iocomplete);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
bufferedOutputStream->Close();
|
||||
outputStream->Close();
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
}
|
||||
return NS_OK;
|
||||
return Append(aInputStream, outputStream);
|
||||
}
|
||||
|
||||
nsresult
|
||||
|
@ -1146,6 +1115,62 @@ DeviceStorageFile::Write(InfallibleTArray<uint8_t>& aBits)
|
|||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
DeviceStorageFile::Append(nsIInputStream* aInputStream)
|
||||
{
|
||||
if (!aInputStream || !mFile) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIOutputStream> outputStream;
|
||||
NS_NewLocalFileOutputStream(getter_AddRefs(outputStream), mFile, PR_WRONLY | PR_CREATE_FILE | PR_APPEND, -1, 0);
|
||||
|
||||
if (!outputStream) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
return Append(aInputStream, outputStream);
|
||||
}
|
||||
|
||||
|
||||
nsresult
|
||||
DeviceStorageFile::Append(nsIInputStream* aInputStream, nsIOutputStream* aOutputStream)
|
||||
{
|
||||
uint64_t bufSize = 0;
|
||||
aInputStream->Available(&bufSize);
|
||||
|
||||
nsCOMPtr<nsIOutputStream> bufferedOutputStream;
|
||||
nsresult rv = NS_NewBufferedOutputStream(getter_AddRefs(bufferedOutputStream),
|
||||
aOutputStream,
|
||||
4096*4);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
while (bufSize) {
|
||||
uint32_t wrote;
|
||||
rv = bufferedOutputStream->WriteFrom(
|
||||
aInputStream,
|
||||
static_cast<uint32_t>(std::min<uint64_t>(bufSize, UINT32_MAX)),
|
||||
&wrote);
|
||||
if (NS_FAILED(rv)) {
|
||||
break;
|
||||
}
|
||||
bufSize -= wrote;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIRunnable> iocomplete = new IOEventComplete(this, "modified");
|
||||
rv = NS_DispatchToMainThread(iocomplete);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
bufferedOutputStream->Close();
|
||||
aOutputStream->Close();
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
DeviceStorageFile::Remove()
|
||||
{
|
||||
|
@ -2373,13 +2398,16 @@ class WriteFileEvent : public nsRunnable
|
|||
public:
|
||||
WriteFileEvent(nsIDOMBlob* aBlob,
|
||||
DeviceStorageFile *aFile,
|
||||
already_AddRefed<DOMRequest> aRequest)
|
||||
already_AddRefed<DOMRequest> aRequest,
|
||||
int32_t aRequestType)
|
||||
: mBlob(aBlob)
|
||||
, mFile(aFile)
|
||||
, mRequest(aRequest)
|
||||
, mRequestType(aRequestType)
|
||||
{
|
||||
MOZ_ASSERT(mFile);
|
||||
MOZ_ASSERT(mRequest);
|
||||
MOZ_ASSERT(mRequestType);
|
||||
}
|
||||
|
||||
~WriteFileEvent() {}
|
||||
|
@ -2393,17 +2421,33 @@ public:
|
|||
|
||||
bool check = false;
|
||||
mFile->mFile->Exists(&check);
|
||||
if (check) {
|
||||
nsresult rv;
|
||||
|
||||
if (mRequestType == DEVICE_STORAGE_REQUEST_APPEND) {
|
||||
if (!check) {
|
||||
nsCOMPtr<nsIRunnable> event =
|
||||
new PostErrorEvent(mRequest.forget(), POST_ERROR_EVENT_FILE_DOES_NOT_EXIST);
|
||||
return NS_DispatchToMainThread(event);
|
||||
}
|
||||
rv = mFile->Append(stream);
|
||||
}
|
||||
else if (mRequestType == DEVICE_STORAGE_REQUEST_CREATE) {
|
||||
if (check) {
|
||||
nsCOMPtr<nsIRunnable> event =
|
||||
new PostErrorEvent(mRequest.forget(), POST_ERROR_EVENT_FILE_EXISTS);
|
||||
return NS_DispatchToMainThread(event);
|
||||
}
|
||||
rv = mFile->Write(stream);
|
||||
if (NS_FAILED(rv)) {
|
||||
mFile->mFile->Remove(false);
|
||||
}
|
||||
} else {
|
||||
nsCOMPtr<nsIRunnable> event =
|
||||
new PostErrorEvent(mRequest.forget(), POST_ERROR_EVENT_FILE_EXISTS);
|
||||
new PostErrorEvent(mRequest.forget(), POST_ERROR_EVENT_UNKNOWN);
|
||||
return NS_DispatchToMainThread(event);
|
||||
}
|
||||
|
||||
nsresult rv = mFile->Write(stream);
|
||||
|
||||
if (NS_FAILED(rv)) {
|
||||
mFile->mFile->Remove(false);
|
||||
|
||||
nsCOMPtr<nsIRunnable> event =
|
||||
new PostErrorEvent(mRequest.forget(), POST_ERROR_EVENT_UNKNOWN);
|
||||
return NS_DispatchToMainThread(event);
|
||||
|
@ -2420,8 +2464,10 @@ private:
|
|||
nsCOMPtr<nsIDOMBlob> mBlob;
|
||||
nsRefPtr<DeviceStorageFile> mFile;
|
||||
nsRefPtr<DOMRequest> mRequest;
|
||||
int32_t mRequestType;
|
||||
};
|
||||
|
||||
|
||||
class ReadFileEvent : public nsRunnable
|
||||
{
|
||||
public:
|
||||
|
@ -2841,7 +2887,49 @@ public:
|
|||
->SendPDeviceStorageRequestConstructor(child, params);
|
||||
return NS_OK;
|
||||
}
|
||||
r = new WriteFileEvent(mBlob, mFile, mRequest.forget());
|
||||
r = new WriteFileEvent(mBlob, mFile, mRequest.forget(), mRequestType);
|
||||
break;
|
||||
}
|
||||
|
||||
case DEVICE_STORAGE_REQUEST_APPEND:
|
||||
{
|
||||
if (!mBlob || !mFile->mFile) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
DeviceStorageTypeChecker* typeChecker
|
||||
= DeviceStorageTypeChecker::CreateOrGet();
|
||||
if (!typeChecker) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
if (!typeChecker->Check(mFile->mStorageType, mFile->mFile) ||
|
||||
!typeChecker->Check(mFile->mStorageType, mBlob)) {
|
||||
r = new PostErrorEvent(mRequest.forget(),
|
||||
POST_ERROR_EVENT_ILLEGAL_TYPE);
|
||||
return NS_DispatchToCurrentThread(r);
|
||||
}
|
||||
|
||||
if (XRE_GetProcessType() != GeckoProcessType_Default) {
|
||||
BlobChild* actor
|
||||
= ContentChild::GetSingleton()->GetOrCreateActorForBlob(mBlob);
|
||||
if (!actor) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
DeviceStorageAppendParams params;
|
||||
params.blobChild() = actor;
|
||||
params.type() = mFile->mStorageType;
|
||||
params.storageName() = mFile->mStorageName;
|
||||
params.relpath() = mFile->mPath;
|
||||
|
||||
PDeviceStorageRequestChild* child
|
||||
= new DeviceStorageRequestChild(mRequest, mFile);
|
||||
ContentChild::GetSingleton()
|
||||
->SendPDeviceStorageRequestConstructor(child, params);
|
||||
return NS_OK;
|
||||
}
|
||||
r = new WriteFileEvent(mBlob, mFile, mRequest.forget(), mRequestType);
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -3457,6 +3545,23 @@ nsDOMDeviceStorage::AddNamed(nsIDOMBlob *aBlob,
|
|||
already_AddRefed<DOMRequest>
|
||||
nsDOMDeviceStorage::AddNamed(nsIDOMBlob* aBlob, const nsAString& aPath,
|
||||
ErrorResult& aRv)
|
||||
{
|
||||
return AddOrAppendNamed(aBlob, aPath,
|
||||
DEVICE_STORAGE_REQUEST_CREATE, aRv);
|
||||
}
|
||||
|
||||
already_AddRefed<DOMRequest>
|
||||
nsDOMDeviceStorage::AppendNamed(nsIDOMBlob* aBlob, const nsAString& aPath,
|
||||
ErrorResult& aRv)
|
||||
{
|
||||
return AddOrAppendNamed(aBlob, aPath,
|
||||
DEVICE_STORAGE_REQUEST_APPEND, aRv);
|
||||
}
|
||||
|
||||
|
||||
already_AddRefed<DOMRequest>
|
||||
nsDOMDeviceStorage::AddOrAppendNamed(nsIDOMBlob* aBlob, const nsAString& aPath,
|
||||
const int32_t aRequestType, ErrorResult& aRv)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
||||
|
@ -3493,7 +3598,9 @@ nsDOMDeviceStorage::AddNamed(nsIDOMBlob* aBlob, const nsAString& aPath,
|
|||
}
|
||||
return request.forget();
|
||||
}
|
||||
return ds->AddNamed(aBlob, storagePath, aRv);
|
||||
|
||||
return ds->AddOrAppendNamed(aBlob, storagePath,
|
||||
aRequestType, aRv);
|
||||
}
|
||||
|
||||
nsRefPtr<DOMRequest> request = new DOMRequest(win);
|
||||
|
@ -3506,9 +3613,13 @@ nsDOMDeviceStorage::AddNamed(nsIDOMBlob* aBlob, const nsAString& aPath,
|
|||
} else if (!typeChecker->Check(mStorageType, dsf->mFile) ||
|
||||
!typeChecker->Check(mStorageType, aBlob)) {
|
||||
r = new PostErrorEvent(request, POST_ERROR_EVENT_ILLEGAL_TYPE);
|
||||
} else {
|
||||
r = new DeviceStorageRequest(DEVICE_STORAGE_REQUEST_CREATE,
|
||||
} else if (aRequestType == DEVICE_STORAGE_REQUEST_APPEND ||
|
||||
aRequestType == DEVICE_STORAGE_REQUEST_CREATE) {
|
||||
r = new DeviceStorageRequest(DeviceStorageRequestType(aRequestType),
|
||||
win, mPrincipal, dsf, request, aBlob);
|
||||
} else {
|
||||
aRv.Throw(NS_ERROR_UNEXPECTED);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
rv = NS_DispatchToCurrentThread(r);
|
||||
|
|
|
@ -45,6 +45,7 @@ class ErrorResult;
|
|||
enum DeviceStorageRequestType {
|
||||
DEVICE_STORAGE_REQUEST_READ,
|
||||
DEVICE_STORAGE_REQUEST_WRITE,
|
||||
DEVICE_STORAGE_REQUEST_APPEND,
|
||||
DEVICE_STORAGE_REQUEST_CREATE,
|
||||
DEVICE_STORAGE_REQUEST_DELETE,
|
||||
DEVICE_STORAGE_REQUEST_WATCH,
|
||||
|
|
|
@ -30,3 +30,4 @@ support-files = devicestorage_common.js
|
|||
[test_fs_get.html]
|
||||
[test_fs_remove.html]
|
||||
[test_fs_createFile.html]
|
||||
[test_fs_appendFile.html]
|
||||
|
|
|
@ -0,0 +1,90 @@
|
|||
<!--
|
||||
Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/
|
||||
-->
|
||||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<!--
|
||||
https://bugzilla.mozilla.org/show_bug.cgi?id=855952
|
||||
-->
|
||||
<head>
|
||||
<title>Test for basic sanity of the device storage API </title>
|
||||
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<script type="text/javascript" src="devicestorage_common.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=855952">Mozilla Bug 855952</a>
|
||||
<p id="display"></p>
|
||||
<div id="content" style="display: none">
|
||||
|
||||
</div>
|
||||
<pre id="test">
|
||||
<script class="testbody" type="text/javascript">
|
||||
|
||||
var file = new Blob(["This is a text file."], {type: "text/plain"});
|
||||
var appendFile = new Blob([" Another text file."], {type: "text/plain"});
|
||||
|
||||
devicestorage_setup();
|
||||
|
||||
function deleteSuccess(e) {
|
||||
devicestorage_cleanup();
|
||||
}
|
||||
|
||||
function deleteError(e) {
|
||||
ok(false, "deleteError was called : " + e.target.error.name);
|
||||
devicestorage_cleanup();
|
||||
}
|
||||
|
||||
function appendSuccess(e) {
|
||||
ok(true, "appendSuccess was called.");
|
||||
|
||||
request = gStorage.delete(e.target.result)
|
||||
request.onsuccess = deleteSuccess;
|
||||
request.onerror = deleteError;
|
||||
}
|
||||
|
||||
function appendError(e) {
|
||||
ok(false, "appendError was called.");
|
||||
devicestorage_cleanup();
|
||||
}
|
||||
|
||||
function addSuccess(e) {
|
||||
ok(true, "addSuccess was called");
|
||||
|
||||
request = gStorage.appendNamed(appendFile, e.target.result);
|
||||
ok(request, "Should have a non-null request");
|
||||
|
||||
request.onsuccess = appendSuccess;
|
||||
request.onerror = appendError;
|
||||
}
|
||||
|
||||
function addError(e) {
|
||||
// test file is already exists. clean it up and try again..
|
||||
request = gStorage.delete(e.target.result)
|
||||
request.onsuccess = addFile;
|
||||
}
|
||||
|
||||
function addFile() {
|
||||
// Add a file first
|
||||
request = gStorage.addNamed(file, "devicestorage/append.asc");
|
||||
ok(request, "Should have a non-null request");
|
||||
request.onsuccess = addSuccess;
|
||||
request.onerror = addError;
|
||||
}
|
||||
|
||||
function runtest() {
|
||||
addFile();
|
||||
}
|
||||
|
||||
var gStorage = navigator.getDeviceStorage("sdcard");
|
||||
ok(navigator.getDeviceStorage, "Should have getDeviceStorage");
|
||||
ok(gStorage, "Should get storage from sdcard");
|
||||
runtest();
|
||||
|
||||
</script>
|
||||
</pre>
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -10,7 +10,7 @@
|
|||
#include "nsIOutputStream.h"
|
||||
#include "nsString.h"
|
||||
|
||||
template <class> class already_AddRefed;
|
||||
template <class> struct already_AddRefed;
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
|
|
@ -118,6 +118,14 @@ struct DeviceStorageAddParams
|
|||
PBlob blob;
|
||||
};
|
||||
|
||||
struct DeviceStorageAppendParams
|
||||
{
|
||||
nsString type;
|
||||
nsString storageName;
|
||||
nsString relpath;
|
||||
PBlob blob;
|
||||
};
|
||||
|
||||
struct DeviceStorageCreateFdParams
|
||||
{
|
||||
nsString type;
|
||||
|
@ -151,6 +159,7 @@ struct DeviceStorageEnumerationParams
|
|||
union DeviceStorageParams
|
||||
{
|
||||
DeviceStorageAddParams;
|
||||
DeviceStorageAppendParams;
|
||||
DeviceStorageCreateFdParams;
|
||||
DeviceStorageGetParams;
|
||||
DeviceStorageDeleteParams;
|
||||
|
|
|
@ -82,9 +82,9 @@ let NotificationDB = {
|
|||
var promise = OS.File.read(NOTIFICATION_STORE_PATH, { encoding: "utf-8"});
|
||||
return promise.then(
|
||||
function onSuccess(data) {
|
||||
// JSON parsing failure will get handled by a later catch in the promise
|
||||
// chain
|
||||
this.notifications = JSON.parse(data);
|
||||
if (data.length > 0) {
|
||||
this.notifications = JSON.parse(data);
|
||||
}
|
||||
// populate the list of notifications by tag
|
||||
if (this.notifications) {
|
||||
for (var origin in this.notifications) {
|
||||
|
|
|
@ -150,9 +150,12 @@ NotificationStorage.prototype = {
|
|||
} catch (e) {
|
||||
debug("Error calling callback done: " + e);
|
||||
}
|
||||
case kMessageNotificationSaveOk:
|
||||
case kMessageNotificationDeleteOk:
|
||||
debug("Error received when treating: '" + message.name + "': " + message.data.errorMsg);
|
||||
case kMessageNotificationSaveKo:
|
||||
case kMessageNotificationDeleteKo:
|
||||
if (DEBUG) {
|
||||
debug("Error received when treating: '" + message.name +
|
||||
"': " + message.data.errorMsg);
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
|
|
|
@ -0,0 +1,63 @@
|
|||
"use strict";
|
||||
|
||||
const Cu = Components.utils;
|
||||
|
||||
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||
Cu.import("resource://gre/modules/Services.jsm");
|
||||
|
||||
XPCOMUtils.defineLazyServiceGetter(this, "cpmm",
|
||||
"@mozilla.org/childprocessmessagemanager;1",
|
||||
"nsIMessageSender");
|
||||
|
||||
let systemNotification = {
|
||||
origin: "app://system.gaiamobile.org/manifest.webapp",
|
||||
id: "{2bc883bf-2809-4432-b0f4-f54e10372764}",
|
||||
title: "SystemNotification:" + Date.now(),
|
||||
dir: "auto",
|
||||
lang: "",
|
||||
body: "System notification body",
|
||||
tag: "",
|
||||
icon: "icon.png"
|
||||
};
|
||||
|
||||
let calendarNotification = {
|
||||
origin: "app://calendar.gaiamobile.org/manifest.webapp",
|
||||
id: "{d8d11299-a58e-429b-9a9a-57c562982fbf}",
|
||||
title: "CalendarNotification:" + Date.now(),
|
||||
dir: "auto",
|
||||
lang: "",
|
||||
body: "Calendar notification body",
|
||||
tag: "",
|
||||
icon: "icon.png"
|
||||
};
|
||||
|
||||
// Helper to start the NotificationDB
|
||||
function startNotificationDB() {
|
||||
Cu.import("resource://gre/modules/NotificationDB.jsm");
|
||||
}
|
||||
|
||||
// Helper function to add a listener, send message and treat the reply
|
||||
function addAndSend(msg, reply, callback, payload, runNext = true) {
|
||||
let handler = {
|
||||
receiveMessage: function(message) {
|
||||
if (message.name === reply) {
|
||||
cpmm.removeMessageListener(reply, handler);
|
||||
callback(message);
|
||||
if (runNext) {
|
||||
run_next_test();
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
cpmm.addMessageListener(reply, handler);
|
||||
cpmm.sendAsyncMessage(msg, payload);
|
||||
}
|
||||
|
||||
// helper fonction, comparing two notifications
|
||||
function compareNotification(notif1, notif2) {
|
||||
// retrieved notification should be the second one sent
|
||||
for (let prop in notif1) {
|
||||
// compare each property
|
||||
do_check_eq(notif1[prop], notif2[prop]);
|
||||
}
|
||||
}
|
|
@ -1,66 +1,11 @@
|
|||
const Cu = Components.utils;
|
||||
|
||||
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||
Cu.import("resource://gre/modules/Services.jsm");
|
||||
|
||||
XPCOMUtils.defineLazyServiceGetter(this, "cpmm",
|
||||
"@mozilla.org/childprocessmessagemanager;1",
|
||||
"nsIMessageSender");
|
||||
|
||||
let systemNotification = {
|
||||
origin: "app://system.gaiamobile.org/manifest.webapp",
|
||||
id: "{2bc883bf-2809-4432-b0f4-f54e10372764}",
|
||||
title: "SystemNotification:" + Date.now(),
|
||||
dir: "auto",
|
||||
lang: "",
|
||||
body: "System notification body",
|
||||
tag: "",
|
||||
icon: "icon.png"
|
||||
};
|
||||
|
||||
let calendarNotification = {
|
||||
origin: "app://calendar.gaiamobile.org/manifest.webapp",
|
||||
id: "{d8d11299-a58e-429b-9a9a-57c562982fbf}",
|
||||
title: "CalendarNotification:" + Date.now(),
|
||||
dir: "auto",
|
||||
lang: "",
|
||||
body: "Calendar notification body",
|
||||
tag: "",
|
||||
icon: "icon.png"
|
||||
};
|
||||
"use strict";
|
||||
|
||||
function run_test() {
|
||||
do_get_profile();
|
||||
Cu.import("resource://gre/modules/NotificationDB.jsm");
|
||||
startNotificationDB();
|
||||
run_next_test();
|
||||
}
|
||||
|
||||
// Helper function to add a listener, send message and treat the reply
|
||||
function addAndSend(msg, reply, callback, payload, runNext = true) {
|
||||
let handler = {
|
||||
receiveMessage: function(message) {
|
||||
if (message.name === reply) {
|
||||
cpmm.removeMessageListener(reply, handler);
|
||||
callback(message);
|
||||
if (runNext) {
|
||||
run_next_test();
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
cpmm.addMessageListener(reply, handler);
|
||||
cpmm.sendAsyncMessage(msg, payload);
|
||||
}
|
||||
|
||||
// helper fonction, comparing two notifications
|
||||
function compareNotification(notif1, notif2) {
|
||||
// retrieved notification should be the second one sent
|
||||
for (let prop in notif1) {
|
||||
// compare each property
|
||||
do_check_eq(notif1[prop], notif2[prop]);
|
||||
}
|
||||
}
|
||||
|
||||
// Get one notification, none exists
|
||||
add_test(function test_get_none() {
|
||||
let requestID = 0;
|
||||
|
|
|
@ -0,0 +1,56 @@
|
|||
"use strict";
|
||||
|
||||
function run_test() {
|
||||
do_get_profile();
|
||||
run_next_test();
|
||||
}
|
||||
|
||||
/// For bug 1024090: test edge case of notificationstore.json
|
||||
add_test(function test_bug1024090_purge() {
|
||||
Cu.import("resource://gre/modules/osfile.jsm");
|
||||
const NOTIFICATION_STORE_PATH =
|
||||
OS.Path.join(OS.Constants.Path.profileDir, "notificationstore.json");
|
||||
let cleanup = OS.File.writeAtomic(NOTIFICATION_STORE_PATH, "");
|
||||
cleanup.then(
|
||||
function onSuccess() {
|
||||
ok(true, "Notification database cleaned.");
|
||||
},
|
||||
function onError(reason) {
|
||||
ok(false, "Notification database error when cleaning: " + reason);
|
||||
}
|
||||
).then(function next() {
|
||||
do_print("Cleanup steps completed: " + NOTIFICATION_STORE_PATH);
|
||||
startNotificationDB();
|
||||
run_next_test();
|
||||
});
|
||||
});
|
||||
|
||||
// Store one notification
|
||||
add_test(function test_bug1024090_send_one() {
|
||||
let requestID = 1;
|
||||
let msgReply = "Notification:Save:Return:OK";
|
||||
let msgHandler = function(message) {
|
||||
equal(requestID, message.data.requestID, "Checking requestID");
|
||||
};
|
||||
|
||||
addAndSend("Notification:Save", msgReply, msgHandler, {
|
||||
origin: systemNotification.origin,
|
||||
notification: systemNotification,
|
||||
requestID: requestID
|
||||
});
|
||||
});
|
||||
|
||||
// Get one notification, one exists
|
||||
add_test(function test_bug1024090_get_one() {
|
||||
let requestID = 2;
|
||||
let msgReply = "Notification:GetAll:Return:OK";
|
||||
let msgHandler = function(message) {
|
||||
equal(requestID, message.data.requestID, "Checking requestID");
|
||||
equal(1, message.data.notifications.length, "One notification stored");
|
||||
};
|
||||
|
||||
addAndSend("Notification:GetAll", msgReply, msgHandler, {
|
||||
origin: systemNotification.origin,
|
||||
requestID: requestID
|
||||
});
|
||||
});
|
|
@ -1,5 +1,6 @@
|
|||
[DEFAULT]
|
||||
head =
|
||||
head = common_test_notificationdb.js
|
||||
tail =
|
||||
|
||||
[test_notificationdb.js]
|
||||
[test_notificationdb_bug1024090.js]
|
||||
|
|
|
@ -15,6 +15,20 @@ interface DeviceStorage : EventTarget {
|
|||
[Throws]
|
||||
DOMRequest? addNamed(Blob? aBlob, DOMString aName);
|
||||
|
||||
/**
|
||||
* Append data to a given file.
|
||||
* If the file doesn't exist, a "NotFoundError" event will be dispatched.
|
||||
* In the same time, it is a request.onerror case.
|
||||
* If the file exists, it will be opened with the following permission:
|
||||
* "PR_WRONLY|PR_CREATE_FILE|PR_APPEND".
|
||||
* The function will return null when blob file is null and other unexpected situations.
|
||||
* @parameter aBlob: A Blob object representing the data to append
|
||||
* @parameter aName: A string representing the full name (path + file name) of the file
|
||||
* to append data to.
|
||||
*/
|
||||
[Throws]
|
||||
DOMRequest? appendNamed(Blob? aBlob, DOMString aName);
|
||||
|
||||
[Throws]
|
||||
DOMRequest get(DOMString aName);
|
||||
[Throws]
|
||||
|
|
|
@ -28,6 +28,7 @@ EXPORTS.mozilla.gfx += [
|
|||
'Rect.h',
|
||||
'Scale.h',
|
||||
'ScaleFactor.h',
|
||||
'SourceSurfaceCairo.h',
|
||||
'Tools.h',
|
||||
'Types.h',
|
||||
'UserData.h',
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
#include "GLTextureImage.h"
|
||||
#include "GLContext.h"
|
||||
#include "gfxContext.h"
|
||||
#include "gfxImageSurface.h"
|
||||
#include "gfxPlatform.h"
|
||||
#include "gfxUtils.h"
|
||||
#include "gfx2DGlue.h"
|
||||
|
|
|
@ -14,6 +14,7 @@
|
|||
#include "LayerSorter.h" // for SortLayersBy3DZOrder
|
||||
#include "LayersLogging.h" // for AppendToString
|
||||
#include "ReadbackLayer.h" // for ReadbackLayer
|
||||
#include "gfxImageSurface.h"
|
||||
#include "gfxPlatform.h" // for gfxPlatform
|
||||
#include "gfxUtils.h" // for gfxUtils, etc
|
||||
#include "gfx2DGlue.h"
|
||||
|
|
|
@ -127,6 +127,7 @@ static int gCMSIntent = QCMS_INTENT_DEFAULT;
|
|||
static void ShutdownCMS();
|
||||
|
||||
#include "mozilla/gfx/2D.h"
|
||||
#include "mozilla/gfx/SourceSurfaceCairo.h"
|
||||
using namespace mozilla::gfx;
|
||||
|
||||
/* Class to listen for pref changes so that chrome code can dynamically
|
||||
|
@ -629,56 +630,12 @@ void SourceSurfaceDestroyed(void *aData)
|
|||
delete static_cast<DependentSourceSurfaceUserData*>(aData);
|
||||
}
|
||||
|
||||
#if MOZ_TREE_CAIRO
|
||||
void SourceSnapshotDetached(cairo_surface_t *nullSurf)
|
||||
{
|
||||
gfxImageSurface* origSurf =
|
||||
static_cast<gfxImageSurface*>(cairo_surface_get_user_data(nullSurf, &kSourceSurface));
|
||||
|
||||
origSurf->SetData(&kSourceSurface, nullptr, nullptr);
|
||||
}
|
||||
#else
|
||||
void SourceSnapshotDetached(void *nullSurf)
|
||||
{
|
||||
gfxImageSurface* origSurf = static_cast<gfxImageSurface*>(nullSurf);
|
||||
origSurf->SetData(&kSourceSurface, nullptr, nullptr);
|
||||
}
|
||||
#endif
|
||||
|
||||
void
|
||||
gfxPlatform::ClearSourceSurfaceForSurface(gfxASurface *aSurface)
|
||||
{
|
||||
aSurface->SetData(&kSourceSurface, nullptr, nullptr);
|
||||
}
|
||||
|
||||
static TemporaryRef<DataSourceSurface>
|
||||
CopySurface(gfxASurface* aSurface)
|
||||
{
|
||||
const nsIntSize size = aSurface->GetSize();
|
||||
gfxImageFormat format = gfxPlatform::GetPlatform()->OptimalFormatForContent(aSurface->GetContentType());
|
||||
RefPtr<DataSourceSurface> data =
|
||||
Factory::CreateDataSourceSurface(ToIntSize(size),
|
||||
ImageFormatToSurfaceFormat(format));
|
||||
if (!data) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
DataSourceSurface::MappedSurface map;
|
||||
DebugOnly<bool> result = data->Map(DataSourceSurface::WRITE, &map);
|
||||
MOZ_ASSERT(result, "Should always succeed mapping raw data surfaces!");
|
||||
|
||||
nsRefPtr<gfxImageSurface> image = new gfxImageSurface(map.mData, size, map.mStride, format);
|
||||
nsRefPtr<gfxContext> ctx = new gfxContext(image);
|
||||
|
||||
ctx->SetSource(aSurface);
|
||||
ctx->SetOperator(gfxContext::OPERATOR_SOURCE);
|
||||
ctx->Paint();
|
||||
|
||||
data->Unmap();
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
/* static */ TemporaryRef<SourceSurface>
|
||||
gfxPlatform::GetSourceSurfaceForSurface(DrawTarget *aTarget, gfxASurface *aSurface)
|
||||
{
|
||||
|
@ -687,9 +644,8 @@ gfxPlatform::GetSourceSurfaceForSurface(DrawTarget *aTarget, gfxASurface *aSurfa
|
|||
}
|
||||
|
||||
if (!aTarget) {
|
||||
if (gfxPlatform::GetPlatform()->ScreenReferenceDrawTarget()) {
|
||||
aTarget = gfxPlatform::GetPlatform()->ScreenReferenceDrawTarget();
|
||||
} else {
|
||||
aTarget = gfxPlatform::GetPlatform()->ScreenReferenceDrawTarget();
|
||||
if (!aTarget) {
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
@ -715,6 +671,33 @@ gfxPlatform::GetSourceSurfaceForSurface(DrawTarget *aTarget, gfxASurface *aSurfa
|
|||
format = SurfaceFormat::B8G8R8A8;
|
||||
}
|
||||
|
||||
if (aTarget->GetType() == BackendType::CAIRO) {
|
||||
// If we're going to be used with a CAIRO DrawTarget, then just create a
|
||||
// SourceSurfaceCairo since we don't know the underlying type of the CAIRO
|
||||
// DrawTarget and can't pick a better surface type. Doing this also avoids
|
||||
// readback of aSurface's surface into memory if, for example, aSurface
|
||||
// wraps an xlib cairo surface (which can be important to avoid a major
|
||||
// slowdown).
|
||||
NativeSurface surf;
|
||||
surf.mFormat = format;
|
||||
surf.mType = NativeSurfaceType::CAIRO_SURFACE;
|
||||
surf.mSurface = aSurface->CairoSurface();
|
||||
surf.mSize = ToIntSize(aSurface->GetSize());
|
||||
// We return here regardless of whether CreateSourceSurfaceFromNativeSurface
|
||||
// succeeds or not since we don't expect to be able to do any better below
|
||||
// if it fails.
|
||||
//
|
||||
// Note that the returned SourceSurfaceCairo holds a strong reference to
|
||||
// the cairo_surface_t* that it wraps, which essencially means it holds a
|
||||
// strong reference to aSurface since aSurface shares its
|
||||
// cairo_surface_t*'s reference count variable. As a result we can't cache
|
||||
// srcBuffer on aSurface (see below) since aSurface would then hold a
|
||||
// strong reference back to srcBuffer, creating a reference loop and a
|
||||
// memory leak. Not caching is fine since wrapping is cheap enough (no
|
||||
// copying) so we can just wrap again next time we're called.
|
||||
return aTarget->CreateSourceSurfaceFromNativeSurface(surf);
|
||||
}
|
||||
|
||||
RefPtr<SourceSurface> srcBuffer;
|
||||
|
||||
#ifdef XP_WIN
|
||||
|
@ -730,48 +713,69 @@ gfxPlatform::GetSourceSurfaceForSurface(DrawTarget *aTarget, gfxASurface *aSurfa
|
|||
dt->Flush();
|
||||
}
|
||||
srcBuffer = aTarget->CreateSourceSurfaceFromNativeSurface(surf);
|
||||
} else
|
||||
}
|
||||
#endif
|
||||
if (aSurface->CairoSurface() && aTarget->GetType() == BackendType::CAIRO) {
|
||||
// If this is an xlib cairo surface we don't want to fetch it into memory
|
||||
// because this is a major slow down.
|
||||
// Currently no other DrawTarget types implement CreateSourceSurfaceFromNativeSurface
|
||||
|
||||
if (!srcBuffer) {
|
||||
// If aSurface wraps data, we can create a SourceSurfaceRawData that wraps
|
||||
// the same data, then optimize it for aTarget:
|
||||
RefPtr<DataSourceSurface> surf = GetWrappedDataSourceSurface(aSurface);
|
||||
if (surf) {
|
||||
srcBuffer = aTarget->OptimizeSourceSurface(surf);
|
||||
if (srcBuffer == surf) {
|
||||
// GetWrappedDataSourceSurface returns a SourceSurface that holds a
|
||||
// strong reference to aSurface since it wraps aSurface's data and
|
||||
// needs it to stay alive. As a result we can't cache srcBuffer on
|
||||
// aSurface (below) since aSurface would then hold a strong reference
|
||||
// back to srcBuffer, creating a reference loop and a memory leak. Not
|
||||
// caching is fine since wrapping is cheap enough (no copying) so we
|
||||
// can just wrap again next time we're called.
|
||||
//
|
||||
// Note that the check below doesn't catch this since srcBuffer will be a
|
||||
// SourceSurfaceRawData object (even if aSurface is not a gfxImageSurface
|
||||
// object), which is why we need this separate check.
|
||||
return srcBuffer.forget();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!srcBuffer) {
|
||||
MOZ_ASSERT(aTarget->GetType() != BackendType::CAIRO,
|
||||
"We already tried CreateSourceSurfaceFromNativeSurface with a "
|
||||
"DrawTargetCairo above");
|
||||
// We've run out of performant options. We now try creating a SourceSurface
|
||||
// using a temporary DrawTargetCairo and then optimizing it to aTarget's
|
||||
// actual type. The CreateSourceSurfaceFromNativeSurface() call will
|
||||
// likely create a DataSourceSurface (possibly involving copying and/or
|
||||
// readback), and the OptimizeSourceSurface may well copy again and upload
|
||||
// to the GPU. So, while this code path is rarely hit, hitting it may be
|
||||
// very slow.
|
||||
NativeSurface surf;
|
||||
surf.mFormat = format;
|
||||
surf.mType = NativeSurfaceType::CAIRO_SURFACE;
|
||||
surf.mSurface = aSurface->CairoSurface();
|
||||
surf.mSize = ToIntSize(aSurface->GetSize());
|
||||
srcBuffer = aTarget->CreateSourceSurfaceFromNativeSurface(surf);
|
||||
|
||||
RefPtr<DrawTarget> drawTarget =
|
||||
Factory::CreateDrawTarget(BackendType::CAIRO, IntSize(1, 1), format);
|
||||
srcBuffer = drawTarget->CreateSourceSurfaceFromNativeSurface(surf);
|
||||
if (srcBuffer) {
|
||||
// It's cheap enough to make a new one so we won't keep it around and
|
||||
// keeping it creates a cycle.
|
||||
return srcBuffer.forget();
|
||||
srcBuffer = aTarget->OptimizeSourceSurface(srcBuffer);
|
||||
}
|
||||
}
|
||||
|
||||
if (!srcBuffer) {
|
||||
nsRefPtr<gfxImageSurface> imgSurface = aSurface->GetAsImageSurface();
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
RefPtr<DataSourceSurface> dataSurf;
|
||||
|
||||
if (imgSurface) {
|
||||
dataSurf = GetWrappedDataSourceSurface(aSurface);
|
||||
} else {
|
||||
dataSurf = CopySurface(aSurface);
|
||||
}
|
||||
|
||||
if (!dataSurf) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
srcBuffer = aTarget->OptimizeSourceSurface(dataSurf);
|
||||
|
||||
if (imgSurface && srcBuffer == dataSurf) {
|
||||
// Our wrapping surface will hold a reference to its image surface. We cause
|
||||
// a reference cycle if we add it to the cache. And caching it is pretty
|
||||
// pointless since we'll just wrap it again next use.
|
||||
return srcBuffer.forget();
|
||||
}
|
||||
if ((srcBuffer->GetType() == SurfaceType::CAIRO &&
|
||||
static_cast<SourceSurfaceCairo*>(srcBuffer.get())->GetSurface() ==
|
||||
aSurface->CairoSurface()) ||
|
||||
(srcBuffer->GetType() == SurfaceType::CAIRO_IMAGE &&
|
||||
static_cast<DataSourceSurfaceCairo*>(srcBuffer.get())->GetSurface() ==
|
||||
aSurface->CairoSurface())) {
|
||||
// See the "Note that the returned SourceSurfaceCairo..." comment above.
|
||||
return srcBuffer.forget();
|
||||
}
|
||||
|
||||
// Add user data to aSurface so we can cache lookups in the future.
|
||||
|
|
|
@ -3411,6 +3411,22 @@ RasterImage::DecodePool::DecodeJob::Run()
|
|||
return NS_OK;
|
||||
}
|
||||
|
||||
RasterImage::DecodePool::DecodeJob::~DecodeJob()
|
||||
{
|
||||
if (gMultithreadedDecoding) {
|
||||
// Dispatch mImage to main thread to prevent mImage from being destructed by decode thread.
|
||||
nsCOMPtr<nsIThread> mainThread = do_GetMainThread();
|
||||
NS_WARN_IF_FALSE(mainThread, "Couldn't get the main thread!");
|
||||
if (mainThread) {
|
||||
// Handle ambiguous nsISupports inheritance
|
||||
RasterImage* rawImg = nullptr;
|
||||
mImage.swap(rawImg);
|
||||
DebugOnly<nsresult> rv = NS_ProxyRelease(mainThread, NS_ISUPPORTS_CAST(ImageResource*, rawImg));
|
||||
MOZ_ASSERT(NS_SUCCEEDED(rv), "Failed to proxy release to main thread");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
nsresult
|
||||
RasterImage::DecodePool::DecodeUntilSizeAvailable(RasterImage* aImg)
|
||||
{
|
||||
|
|
|
@ -487,6 +487,9 @@ private:
|
|||
|
||||
NS_IMETHOD Run();
|
||||
|
||||
protected:
|
||||
virtual ~DecodeJob();
|
||||
|
||||
private:
|
||||
nsRefPtr<DecodeRequest> mRequest;
|
||||
nsRefPtr<RasterImage> mImage;
|
||||
|
|
|
@ -9,7 +9,7 @@
|
|||
#include "mozilla/Attributes.h"
|
||||
#include "mozilla/ipc/Transport.h"
|
||||
|
||||
template <class> class already_AddRefed;
|
||||
template <class> struct already_AddRefed;
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
|
|
@ -177,8 +177,9 @@ class ConstTwoByteChars : public mozilla::RangedPtr<const jschar>
|
|||
extern Latin1CharsZ
|
||||
LossyTwoByteCharsToNewLatin1CharsZ(js::ThreadSafeContext *cx, TwoByteChars tbchars);
|
||||
|
||||
template <typename CharT>
|
||||
extern UTF8CharsZ
|
||||
TwoByteCharsToNewUTF8CharsZ(js::ThreadSafeContext *cx, TwoByteChars tbchars);
|
||||
CharsToNewUTF8CharsZ(js::ThreadSafeContext *cx, const mozilla::Range<const CharT> chars);
|
||||
|
||||
uint32_t
|
||||
Utf8ToOneUcs4Char(const uint8_t *utf8Buffer, int utf8Length);
|
||||
|
|
|
@ -22,6 +22,9 @@ using namespace js;
|
|||
using mozilla::AddToHash;
|
||||
using mozilla::HashString;
|
||||
using mozilla::Range;
|
||||
using mozilla::RangedPtr;
|
||||
|
||||
using JS::AutoCheckCannotGC;
|
||||
|
||||
// We should be able to assert this for *any* fp->scopeChain().
|
||||
static void
|
||||
|
@ -49,10 +52,11 @@ IsEvalCacheCandidate(JSScript *script)
|
|||
/* static */ HashNumber
|
||||
EvalCacheHashPolicy::hash(const EvalCacheLookup &l)
|
||||
{
|
||||
return AddToHash(HashString(l.str->chars(), l.str->length()),
|
||||
l.callerScript.get(),
|
||||
l.version,
|
||||
l.pc);
|
||||
AutoCheckCannotGC nogc;
|
||||
uint32_t hash = l.str->hasLatin1Chars()
|
||||
? HashString(l.str->latin1Chars(nogc), l.str->length())
|
||||
: HashString(l.str->twoByteChars(nogc), l.str->length());
|
||||
return AddToHash(hash, l.callerScript.get(), l.version, l.pc);
|
||||
}
|
||||
|
||||
/* static */ bool
|
||||
|
@ -145,23 +149,18 @@ enum EvalJSONResult {
|
|||
EvalJSON_NotJSON
|
||||
};
|
||||
|
||||
static EvalJSONResult
|
||||
TryEvalJSON(JSContext *cx, JSScript *callerScript,
|
||||
ConstTwoByteChars chars, size_t length, MutableHandleValue rval)
|
||||
template <typename CharT>
|
||||
static bool
|
||||
EvalStringMightBeJSON(const Range<const CharT> chars)
|
||||
{
|
||||
// If the eval string starts with '(' or '[' and ends with ')' or ']', it may be JSON.
|
||||
// Try the JSON parser first because it's much faster. If the eval string
|
||||
// isn't JSON, JSON parsing will probably fail quickly, so little time
|
||||
// will be lost.
|
||||
//
|
||||
// Don't use the JSON parser if the caller is strict mode code, because in
|
||||
// strict mode object literals must not have repeated properties, and the
|
||||
// JSON parser cheerfully (and correctly) accepts them. If you're parsing
|
||||
// JSON with eval and using strict mode, you deserve to be slow.
|
||||
size_t length = chars.length();
|
||||
if (length > 2 &&
|
||||
((chars[0] == '[' && chars[length - 1] == ']') ||
|
||||
(chars[0] == '(' && chars[length - 1] == ')')) &&
|
||||
(!callerScript || !callerScript->strict()))
|
||||
(chars[0] == '(' && chars[length - 1] == ')')))
|
||||
{
|
||||
// Remarkably, JavaScript syntax is not a superset of JSON syntax:
|
||||
// strings in JavaScript cannot contain the Unicode line and paragraph
|
||||
|
@ -169,27 +168,68 @@ TryEvalJSON(JSContext *cx, JSScript *callerScript,
|
|||
// Rather than force the JSON parser to handle this quirk when used by
|
||||
// eval, we simply don't use the JSON parser when either character
|
||||
// appears in the provided string. See bug 657367.
|
||||
for (const jschar *cp = &chars[1], *end = &chars[length - 2]; ; cp++) {
|
||||
if (*cp == 0x2028 || *cp == 0x2029)
|
||||
break;
|
||||
|
||||
if (cp == end) {
|
||||
bool isArray = (chars[0] == '[');
|
||||
auto jsonChars = isArray
|
||||
? Range<const jschar>(chars.get(), length)
|
||||
: Range<const jschar>(chars.get() + 1U, length - 2);
|
||||
JSONParser<jschar> parser(cx, jsonChars, JSONParserBase::NoError);
|
||||
RootedValue tmp(cx);
|
||||
if (!parser.parse(&tmp))
|
||||
return EvalJSON_Failure;
|
||||
if (tmp.isUndefined())
|
||||
return EvalJSON_NotJSON;
|
||||
rval.set(tmp);
|
||||
return EvalJSON_Success;
|
||||
if (sizeof(CharT) > 1) {
|
||||
for (RangedPtr<const CharT> cp = chars.start() + 1, end = chars.end() - 1;
|
||||
cp < end;
|
||||
cp++)
|
||||
{
|
||||
jschar c = *cp;
|
||||
if (c == 0x2028 || c == 0x2029)
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
return EvalJSON_NotJSON;
|
||||
return false;
|
||||
}
|
||||
|
||||
template <typename CharT>
|
||||
static EvalJSONResult
|
||||
ParseEvalStringAsJSON(JSContext *cx, const Range<const CharT> chars, MutableHandleValue rval)
|
||||
{
|
||||
size_t len = chars.length();
|
||||
MOZ_ASSERT((chars[0] == '(' && chars[len - 1] == ')') ||
|
||||
(chars[0] == '[' && chars[len - 1] == ']'));
|
||||
|
||||
auto jsonChars = (chars[0] == '[')
|
||||
? chars
|
||||
: Range<const CharT>(chars.start().get() + 1U, len - 2);
|
||||
|
||||
JSONParser<CharT> parser(cx, jsonChars, JSONParserBase::NoError);
|
||||
if (!parser.parse(rval))
|
||||
return EvalJSON_Failure;
|
||||
|
||||
return rval.isUndefined() ? EvalJSON_NotJSON : EvalJSON_Success;
|
||||
}
|
||||
|
||||
static EvalJSONResult
|
||||
TryEvalJSON(JSContext *cx, JSScript *callerScript, JSFlatString *str, MutableHandleValue rval)
|
||||
{
|
||||
// Don't use the JSON parser if the caller is strict mode code, because in
|
||||
// strict mode object literals must not have repeated properties, and the
|
||||
// JSON parser cheerfully (and correctly) accepts them. If you're parsing
|
||||
// JSON with eval and using strict mode, you deserve to be slow.
|
||||
if (callerScript && callerScript->strict())
|
||||
return EvalJSON_NotJSON;
|
||||
|
||||
if (str->hasLatin1Chars()) {
|
||||
AutoCheckCannotGC nogc;
|
||||
if (!EvalStringMightBeJSON(str->latin1Range(nogc)))
|
||||
return EvalJSON_NotJSON;
|
||||
} else {
|
||||
AutoCheckCannotGC nogc;
|
||||
if (!EvalStringMightBeJSON(str->twoByteRange(nogc)))
|
||||
return EvalJSON_NotJSON;
|
||||
}
|
||||
|
||||
AutoStableStringChars flatChars(cx, str);
|
||||
if (!flatChars.init())
|
||||
return EvalJSON_Failure;
|
||||
|
||||
return flatChars.isLatin1()
|
||||
? ParseEvalStringAsJSON(cx, flatChars.latin1Range(), rval)
|
||||
: ParseEvalStringAsJSON(cx, flatChars.twoByteRange(), rval);
|
||||
}
|
||||
|
||||
// Define subset of ExecuteType so that casting performs the injection.
|
||||
|
@ -261,11 +301,8 @@ EvalKernel(JSContext *cx, const CallArgs &args, EvalType evalType, AbstractFrame
|
|||
if (!flatStr)
|
||||
return false;
|
||||
|
||||
size_t length = flatStr->length();
|
||||
ConstTwoByteChars chars(flatStr->chars(), length);
|
||||
|
||||
RootedScript callerScript(cx, caller ? caller.script() : nullptr);
|
||||
EvalJSONResult ejr = TryEvalJSON(cx, callerScript, chars, length, args.rval());
|
||||
EvalJSONResult ejr = TryEvalJSON(cx, callerScript, flatStr, args.rval());
|
||||
if (ejr != EvalJSON_NotJSON)
|
||||
return ejr == EvalJSON_Success;
|
||||
|
||||
|
@ -297,7 +334,16 @@ EvalKernel(JSContext *cx, const CallArgs &args, EvalType evalType, AbstractFrame
|
|||
.setNoScriptRval(false)
|
||||
.setOriginPrincipals(originPrincipals)
|
||||
.setIntroductionInfo(introducerFilename, "eval", lineno, maybeScript, pcOffset);
|
||||
SourceBufferHolder srcBuf(chars.get(), length, SourceBufferHolder::NoOwnership);
|
||||
|
||||
AutoStableStringChars flatChars(cx, flatStr);
|
||||
if (!flatChars.initTwoByte(cx))
|
||||
return false;
|
||||
|
||||
const jschar *chars = flatChars.twoByteRange().start().get();
|
||||
SourceBufferHolder::Ownership ownership = flatChars.maybeGiveOwnershipToCaller()
|
||||
? SourceBufferHolder::GiveOwnership
|
||||
: SourceBufferHolder::NoOwnership;
|
||||
SourceBufferHolder srcBuf(chars, flatStr->length(), ownership);
|
||||
JSScript *compiled = frontend::CompileScript(cx, &cx->tempLifoAlloc(),
|
||||
scopeobj, callerScript, options,
|
||||
srcBuf, flatStr, staticLevel);
|
||||
|
@ -333,10 +379,7 @@ js::DirectEvalStringFromIon(JSContext *cx,
|
|||
if (!flatStr)
|
||||
return false;
|
||||
|
||||
size_t length = flatStr->length();
|
||||
ConstTwoByteChars chars(flatStr->chars(), length);
|
||||
|
||||
EvalJSONResult ejr = TryEvalJSON(cx, callerScript, chars, length, vp);
|
||||
EvalJSONResult ejr = TryEvalJSON(cx, callerScript, flatStr, vp);
|
||||
if (ejr != EvalJSON_NotJSON)
|
||||
return ejr == EvalJSON_Success;
|
||||
|
||||
|
@ -364,7 +407,16 @@ js::DirectEvalStringFromIon(JSContext *cx,
|
|||
.setNoScriptRval(false)
|
||||
.setOriginPrincipals(originPrincipals)
|
||||
.setIntroductionInfo(introducerFilename, "eval", lineno, maybeScript, pcOffset);
|
||||
SourceBufferHolder srcBuf(chars.get(), length, SourceBufferHolder::NoOwnership);
|
||||
|
||||
AutoStableStringChars flatChars(cx, flatStr);
|
||||
if (!flatChars.initTwoByte(cx))
|
||||
return false;
|
||||
|
||||
const jschar *chars = flatChars.twoByteRange().start().get();
|
||||
SourceBufferHolder::Ownership ownership = flatChars.maybeGiveOwnershipToCaller()
|
||||
? SourceBufferHolder::GiveOwnership
|
||||
: SourceBufferHolder::NoOwnership;
|
||||
SourceBufferHolder srcBuf(chars, flatStr->length(), ownership);
|
||||
JSScript *compiled = frontend::CompileScript(cx, &cx->tempLifoAlloc(),
|
||||
scopeobj, callerScript, options,
|
||||
srcBuf, flatStr, staticLevel);
|
||||
|
|
|
@ -7198,7 +7198,7 @@ Parser<ParseHandler>::objectLiteral()
|
|||
if (!abortIfSyntaxParser())
|
||||
return null();
|
||||
tokenStream.ungetToken();
|
||||
if (!tokenStream.checkForKeyword(atom->charsZ(), atom->length(), nullptr))
|
||||
if (!tokenStream.checkForKeyword(atom, nullptr))
|
||||
return null();
|
||||
PropertyName *name = handler.isName(propname);
|
||||
JS_ASSERT(atom);
|
||||
|
|
|
@ -50,8 +50,9 @@ static const KeywordInfo keywords[] = {
|
|||
|
||||
// Returns a KeywordInfo for the specified characters, or nullptr if the string
|
||||
// is not a keyword.
|
||||
template <typename CharT>
|
||||
static const KeywordInfo *
|
||||
FindKeyword(const jschar *s, size_t length)
|
||||
FindKeyword(const CharT *s, size_t length)
|
||||
{
|
||||
JS_ASSERT(length != 0);
|
||||
|
||||
|
@ -87,30 +88,47 @@ FindKeyword(const jschar *s, size_t length)
|
|||
return nullptr;
|
||||
}
|
||||
|
||||
static const KeywordInfo *
|
||||
FindKeyword(JSLinearString *str)
|
||||
{
|
||||
JS::AutoCheckCannotGC nogc;
|
||||
return str->hasLatin1Chars()
|
||||
? FindKeyword(str->latin1Chars(nogc), str->length())
|
||||
: FindKeyword(str->twoByteChars(nogc), str->length());
|
||||
}
|
||||
|
||||
template <typename CharT>
|
||||
static bool
|
||||
IsIdentifier(const CharT *chars, size_t length)
|
||||
{
|
||||
if (length == 0)
|
||||
return false;
|
||||
|
||||
if (!IsIdentifierStart(*chars))
|
||||
return false;
|
||||
|
||||
const CharT *end = chars + length;
|
||||
while (++chars != end) {
|
||||
if (!IsIdentifierPart(*chars))
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
frontend::IsIdentifier(JSLinearString *str)
|
||||
{
|
||||
const jschar *chars = str->chars();
|
||||
size_t length = str->length();
|
||||
|
||||
if (length == 0)
|
||||
return false;
|
||||
jschar c = *chars;
|
||||
if (!IsIdentifierStart(c))
|
||||
return false;
|
||||
const jschar *end = chars + length;
|
||||
while (++chars != end) {
|
||||
c = *chars;
|
||||
if (!IsIdentifierPart(c))
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
JS::AutoCheckCannotGC nogc;
|
||||
return str->hasLatin1Chars()
|
||||
? ::IsIdentifier(str->latin1Chars(nogc), str->length())
|
||||
: ::IsIdentifier(str->twoByteChars(nogc), str->length());
|
||||
}
|
||||
|
||||
bool
|
||||
frontend::IsKeyword(JSLinearString *str)
|
||||
{
|
||||
return FindKeyword(str->chars(), str->length()) != nullptr;
|
||||
return FindKeyword(str) != nullptr;
|
||||
}
|
||||
|
||||
TokenStream::SourceCoords::SourceCoords(ExclusiveContext *cx, uint32_t ln)
|
||||
|
@ -962,12 +980,8 @@ TokenStream::putIdentInTokenbuf(const jschar *identStart)
|
|||
}
|
||||
|
||||
bool
|
||||
TokenStream::checkForKeyword(const jschar *s, size_t length, TokenKind *ttp)
|
||||
TokenStream::checkForKeyword(const KeywordInfo *kw, TokenKind *ttp)
|
||||
{
|
||||
const KeywordInfo *kw = FindKeyword(s, length);
|
||||
if (!kw)
|
||||
return true;
|
||||
|
||||
if (kw->tokentype == TOK_RESERVED)
|
||||
return reportError(JSMSG_RESERVED_ID, kw->chars);
|
||||
|
||||
|
@ -992,6 +1006,26 @@ TokenStream::checkForKeyword(const jschar *s, size_t length, TokenKind *ttp)
|
|||
return reportStrictModeError(JSMSG_RESERVED_ID, kw->chars);
|
||||
}
|
||||
|
||||
bool
|
||||
TokenStream::checkForKeyword(const jschar *s, size_t length, TokenKind *ttp)
|
||||
{
|
||||
const KeywordInfo *kw = FindKeyword(s, length);
|
||||
if (!kw)
|
||||
return true;
|
||||
|
||||
return checkForKeyword(kw, ttp);
|
||||
}
|
||||
|
||||
bool
|
||||
TokenStream::checkForKeyword(JSAtom *atom, TokenKind *ttp)
|
||||
{
|
||||
const KeywordInfo *kw = FindKeyword(atom);
|
||||
if (!kw)
|
||||
return true;
|
||||
|
||||
return checkForKeyword(kw, ttp);
|
||||
}
|
||||
|
||||
enum FirstCharKind {
|
||||
// A jschar has the 'OneChar' kind if it, by itself, constitutes a valid
|
||||
// token that cannot also be a prefix of a longer token. E.g. ';' has the
|
||||
|
|
|
@ -22,6 +22,8 @@
|
|||
#include "js/Vector.h"
|
||||
#include "vm/RegExpObject.h"
|
||||
|
||||
struct KeywordInfo;
|
||||
|
||||
namespace js {
|
||||
namespace frontend {
|
||||
|
||||
|
@ -655,7 +657,9 @@ class MOZ_STACK_CLASS TokenStream
|
|||
// null, report a SyntaxError ("if is a reserved identifier") and return
|
||||
// false. If ttp is non-null, return true with the keyword's TokenKind in
|
||||
// *ttp.
|
||||
bool checkForKeyword(const KeywordInfo *kw, TokenKind *ttp);
|
||||
bool checkForKeyword(const jschar *s, size_t length, TokenKind *ttp);
|
||||
bool checkForKeyword(JSAtom *atom, TokenKind *ttp);
|
||||
|
||||
// This class maps a userbuf offset (which is 0-indexed) to a line number
|
||||
// (which is 1-indexed) and a column index (which is 0-indexed).
|
||||
|
|
|
@ -366,3 +366,35 @@ if (isAsmJSCompilationAvailable() && isCachingEnabled()) {
|
|||
}
|
||||
|
||||
})();
|
||||
|
||||
/* Implicit "use strict" context in functions with dynamic linking failure */
|
||||
(function () {
|
||||
|
||||
var funcCode = 'function g(x) {\n\
|
||||
x=x|0;\n\
|
||||
return x + 1 | 0;}';
|
||||
var moduleCode = 'function (glob) {\n\
|
||||
"use asm";\n\
|
||||
var fround = glob.Math.fround;\n\
|
||||
' + funcCode + '\n\
|
||||
return g;\n\
|
||||
}',
|
||||
useStrict = '"use strict";';
|
||||
|
||||
var f6 = eval(useStrict + ";\n(" + moduleCode + "({Math:{}}))");
|
||||
|
||||
var expectedToString = funcCode.replace('{', '{\n' + useStrict + '\n')
|
||||
var expectedToSource = expectedToString
|
||||
|
||||
assertEq(f6.toString(), expectedToString);
|
||||
assertEq(f6.toSource(), expectedToSource);
|
||||
|
||||
if (isAsmJSCompilationAvailable() && isCachingEnabled()) {
|
||||
var mf6 = eval("\"use strict\";\n(" + moduleCode + ")");
|
||||
assertEq(isAsmJSModuleLoadedFromCache(mf6), true);
|
||||
var f6 = mf6({Math:{}});
|
||||
assertEq(f6.toString(), expectedToString);
|
||||
assertEq(f6.toSource(), expectedToSource);
|
||||
}
|
||||
|
||||
})();
|
||||
|
|
|
@ -0,0 +1,7 @@
|
|||
function f(x) {
|
||||
x = eval("a = arguments.callee.arguments; 10");
|
||||
}
|
||||
for (var i=0; i<5; i++) {
|
||||
f(5);
|
||||
assertEq(a[0], 10);
|
||||
}
|
|
@ -0,0 +1,10 @@
|
|||
gczeal(8, 2)
|
||||
try {
|
||||
[new String, y]
|
||||
} catch (e) {}
|
||||
r = /()/
|
||||
try {
|
||||
"".replace(r, () => {
|
||||
[]()
|
||||
});
|
||||
} catch(e) {}
|
|
@ -0,0 +1,20 @@
|
|||
function test() {
|
||||
var arr = [
|
||||
toLatin1("abc"),
|
||||
toLatin1("abcd"),
|
||||
toLatin1("123\u00ff")
|
||||
];
|
||||
for (var i = 0; i < arr.length; i++) {
|
||||
for (var j = 0; j < arr.length; j++) {
|
||||
var s1 = arr[i];
|
||||
var s2 = arr[j];
|
||||
var s1tb = "\u1200" + s1;
|
||||
var s2tb = "\u1200" + s2;
|
||||
assertEq(s1 < s2, s1tb < s2tb);
|
||||
assertEq(s1 > s2, s1tb > s2tb);
|
||||
assertEq(s1 <= s2, s1tb <= s2tb);
|
||||
assertEq(s1 >= s2, s1tb >= s2tb);
|
||||
}
|
||||
}
|
||||
}
|
||||
test();
|
|
@ -0,0 +1,22 @@
|
|||
var s = toLatin1("someName");
|
||||
|
||||
// Latin1
|
||||
function f(someName) {
|
||||
someName();
|
||||
}
|
||||
try {
|
||||
f(3);
|
||||
} catch(e) {
|
||||
assertEq(e.message.contains("someName"), true);
|
||||
}
|
||||
|
||||
// TwoByte
|
||||
function g(someName\u1200) {
|
||||
someName\u1200();
|
||||
}
|
||||
try {
|
||||
g(3);
|
||||
} catch(e) {
|
||||
// Note: string is deflated; don't check for the \u1200.
|
||||
assertEq(e.message.contains("someName"), true);
|
||||
}
|
|
@ -0,0 +1,10 @@
|
|||
function f(s) {
|
||||
var x = 3, y = 5;
|
||||
var z = eval(s);
|
||||
assertEq(z, 8);
|
||||
}
|
||||
var s = toLatin1("x + y");
|
||||
f(s); // Latin1
|
||||
f(s);
|
||||
f("x + y;/*\u1200*/"); // TwoByte
|
||||
f("x + y;/*\u1200*/");
|
|
@ -0,0 +1,14 @@
|
|||
var arrLatin1 = [toLatin1("abc1"), toLatin1("abc\u00A0")];
|
||||
assertEq(arrLatin1.join(toLatin1("sep\u00ff")), "abc1sep\xFFabc\xA0");
|
||||
|
||||
var arrTwoByte = [toLatin1("abc2"), "def\u1200"];
|
||||
assertEq(arrTwoByte.join(toLatin1("sep\u00fe")), "abc2sep\xFEdef\u1200");
|
||||
|
||||
assertEq(arrLatin1.join(toLatin1("-")), "abc1-abc\xA0");
|
||||
assertEq(arrTwoByte.join(toLatin1("7")), "abc27def\u1200");
|
||||
|
||||
assertEq(arrLatin1.join("\u1200"), "abc1\u1200abc\xA0");
|
||||
assertEq(arrTwoByte.join("\u1200"), "abc2\u1200def\u1200");
|
||||
|
||||
assertEq(arrLatin1.join("---\u1200"), "abc1---\u1200abc\xA0");
|
||||
assertEq(arrTwoByte.join("---\u1200"), "abc2---\u1200def\u1200");
|
|
@ -32,3 +32,33 @@ function testErrorPos() {
|
|||
}
|
||||
}
|
||||
testErrorPos();
|
||||
|
||||
function testEvalHack() {
|
||||
// Latin1
|
||||
var arr = eval(toLatin1("[1, 2, 3, \"abc\"]"));
|
||||
assertEq(JSON.stringify(arr), '[1,2,3,"abc"]');
|
||||
|
||||
// TwoByte
|
||||
arr = eval("[1, 2, 3, \"abc\u1200\"]");
|
||||
assertEq(JSON.stringify(arr), '[1,2,3,"abc\u1200"]');
|
||||
}
|
||||
testEvalHack();
|
||||
|
||||
function testEvalHackNotJSON() {
|
||||
// Latin1
|
||||
var arr = eval(toLatin1("[]; var q; [1, 2, 3, \"abc\"]"));
|
||||
assertEq(JSON.stringify(arr), '[1,2,3,"abc"]');
|
||||
|
||||
// TwoByte
|
||||
arr = eval("[]; var z; [1, 2, 3, \"abc\u1200\"]");
|
||||
assertEq(JSON.stringify(arr), '[1,2,3,"abc\u1200"]');
|
||||
|
||||
try {
|
||||
eval("[1, 2, 3, \"abc\u2028\"]");
|
||||
throw new Error("U+2028 shouldn't eval");
|
||||
} catch (e) {
|
||||
assertEq(e instanceof SyntaxError, true,
|
||||
"should have thrown a SyntaxError, instead got " + e);
|
||||
}
|
||||
}
|
||||
testEvalHackNotJSON();
|
||||
|
|
|
@ -0,0 +1,24 @@
|
|||
// Latin1
|
||||
var s = toLatin1("abcdef,g,,");
|
||||
var res = s.split(toLatin1(","));
|
||||
assertEq(res[0], "abcdef");
|
||||
assertEq(res[1], "g");
|
||||
assertEq(res[2], "");
|
||||
assertEq(res[3], "");
|
||||
|
||||
s = toLatin1("abcdef,gh,,");
|
||||
res = s.split("\u1200");
|
||||
assertEq(res[0], "abcdef,gh,,");
|
||||
|
||||
// TwoByte
|
||||
s = "abcdef\u1200\u1200,g,,";
|
||||
res = s.split(",");
|
||||
assertEq(res[0], "abcdef\u1200\u1200");
|
||||
assertEq(res[1], "g");
|
||||
assertEq(res[2], "");
|
||||
assertEq(res[3], "");
|
||||
|
||||
res = s.split("\u1200");
|
||||
assertEq(res[0], "abcdef");
|
||||
assertEq(res[1], "");
|
||||
assertEq(res[2], ",g,,");
|
|
@ -607,6 +607,11 @@ HandleDynamicLinkFailure(JSContext *cx, CallArgs args, AsmJSModule &module, Hand
|
|||
.setCompileAndGo(false)
|
||||
.setNoScriptRval(false);
|
||||
|
||||
// The exported function inherits an implicit strict context if the module
|
||||
// also inherited it somehow.
|
||||
if (module.strict())
|
||||
options.strictOption = true;
|
||||
|
||||
SourceBufferHolder srcBuf(src->chars(), end - begin, SourceBufferHolder::NoOwnership);
|
||||
if (!frontend::CompileFunctionBody(cx, &fun, options, formals, srcBuf))
|
||||
return false;
|
||||
|
|
|
@ -2960,10 +2960,11 @@ EqualStringsHelper(JSString *str1, JSString *str2)
|
|||
JS_ASSERT(!str2->isAtom());
|
||||
JS_ASSERT(str1->length() == str2->length());
|
||||
|
||||
const jschar *chars = str2->getChars(nullptr);
|
||||
if (!chars)
|
||||
JSLinearString *str2Linear = str2->ensureLinear(nullptr);
|
||||
if (!str2Linear)
|
||||
return false;
|
||||
return mozilla::PodEqual(str1->asAtom().chars(), chars, str1->length());
|
||||
|
||||
return EqualChars(&str1->asLinear(), str2Linear);
|
||||
}
|
||||
|
||||
bool
|
||||
|
|
|
@ -685,19 +685,20 @@ GetDynamicName(JSContext *cx, JSObject *scopeChain, JSString *str, Value *vp)
|
|||
bool
|
||||
FilterArgumentsOrEval(JSContext *cx, JSString *str)
|
||||
{
|
||||
// getChars() is fallible, but cannot GC: it can only allocate a character
|
||||
// for the flattened string. If this call fails then the calling Ion code
|
||||
// will bailout, resume in the interpreter and likely fail again when
|
||||
// trying to flatten the string and unwind the stack.
|
||||
const jschar *chars = str->getChars(cx);
|
||||
if (!chars)
|
||||
// ensureLinear() is fallible, but cannot GC: it can only allocate a
|
||||
// character buffer for the flattened string. If this call fails then the
|
||||
// calling Ion code will bailout, resume in Baseline and likely fail again
|
||||
// when trying to flatten the string and unwind the stack.
|
||||
JS::AutoCheckCannotGC nogc;
|
||||
JSLinearString *linear = str->ensureLinear(cx);
|
||||
if (!linear)
|
||||
return false;
|
||||
|
||||
static const jschar arguments[] = {'a', 'r', 'g', 'u', 'm', 'e', 'n', 't', 's'};
|
||||
static const jschar eval[] = {'e', 'v', 'a', 'l'};
|
||||
|
||||
return !StringHasPattern(chars, str->length(), arguments, mozilla::ArrayLength(arguments)) &&
|
||||
!StringHasPattern(chars, str->length(), eval, mozilla::ArrayLength(eval));
|
||||
return !StringHasPattern(linear, arguments, mozilla::ArrayLength(arguments)) &&
|
||||
!StringHasPattern(linear, eval, mozilla::ArrayLength(eval));
|
||||
}
|
||||
|
||||
#ifdef JSGC_GENERATIONAL
|
||||
|
|
|
@ -1675,11 +1675,11 @@ Assembler::as_dtm(LoadStore ls, Register rn, uint32_t mask,
|
|||
}
|
||||
|
||||
BufferOffset
|
||||
Assembler::as_Imm32Pool(Register dest, uint32_t value, ARMBuffer::PoolEntry *pe, Condition c)
|
||||
Assembler::as_Imm32Pool(Register dest, uint32_t value, Condition c)
|
||||
{
|
||||
PoolHintPun php;
|
||||
php.phd.init(0, c, PoolHintData::poolDTR, dest);
|
||||
return m_buffer.insertEntry(4, (uint8_t*)&php.raw, int32Pool, (uint8_t*)&value, pe);
|
||||
return m_buffer.insertEntry(4, (uint8_t*)&php.raw, int32Pool, (uint8_t*)&value);
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -1716,12 +1716,12 @@ Assembler::as_BranchPool(uint32_t value, RepatchLabel *label, ARMBuffer::PoolEnt
|
|||
}
|
||||
|
||||
BufferOffset
|
||||
Assembler::as_FImm64Pool(VFPRegister dest, double value, ARMBuffer::PoolEntry *pe, Condition c)
|
||||
Assembler::as_FImm64Pool(VFPRegister dest, double value, Condition c)
|
||||
{
|
||||
JS_ASSERT(dest.isDouble());
|
||||
PoolHintPun php;
|
||||
php.phd.init(0, c, PoolHintData::poolVDTR, dest);
|
||||
return m_buffer.insertEntry(4, (uint8_t*)&php.raw, doublePool, (uint8_t*)&value, pe);
|
||||
return m_buffer.insertEntry(4, (uint8_t*)&php.raw, doublePool, (uint8_t*)&value);
|
||||
}
|
||||
|
||||
struct PaddedFloat32
|
||||
|
@ -1732,7 +1732,7 @@ struct PaddedFloat32
|
|||
JS_STATIC_ASSERT(sizeof(PaddedFloat32) == sizeof(double));
|
||||
|
||||
BufferOffset
|
||||
Assembler::as_FImm32Pool(VFPRegister dest, float value, ARMBuffer::PoolEntry *pe, Condition c)
|
||||
Assembler::as_FImm32Pool(VFPRegister dest, float value, Condition c)
|
||||
{
|
||||
/*
|
||||
* Insert floats into the double pool as they have the same limitations on
|
||||
|
@ -1743,7 +1743,7 @@ Assembler::as_FImm32Pool(VFPRegister dest, float value, ARMBuffer::PoolEntry *pe
|
|||
PoolHintPun php;
|
||||
php.phd.init(0, c, PoolHintData::poolVDTR, dest);
|
||||
PaddedFloat32 pf = { value, 0 };
|
||||
return m_buffer.insertEntry(4, (uint8_t*)&php.raw, doublePool, (uint8_t*)&pf, pe);
|
||||
return m_buffer.insertEntry(4, (uint8_t*)&php.raw, doublePool, (uint8_t*)&pf);
|
||||
}
|
||||
|
||||
// Pool callbacks stuff:
|
||||
|
|
|
@ -1482,14 +1482,14 @@ class Assembler : public AssemblerShared
|
|||
//overwrite a pool entry with new data.
|
||||
void as_WritePoolEntry(Instruction *addr, Condition c, uint32_t data);
|
||||
// load a 32 bit immediate from a pool into a register
|
||||
BufferOffset as_Imm32Pool(Register dest, uint32_t value, ARMBuffer::PoolEntry *pe = nullptr, Condition c = Always);
|
||||
BufferOffset as_Imm32Pool(Register dest, uint32_t value, Condition c = Always);
|
||||
// make a patchable jump that can target the entire 32 bit address space.
|
||||
BufferOffset as_BranchPool(uint32_t value, RepatchLabel *label, ARMBuffer::PoolEntry *pe = nullptr, Condition c = Always);
|
||||
|
||||
// load a 64 bit floating point immediate from a pool into a register
|
||||
BufferOffset as_FImm64Pool(VFPRegister dest, double value, ARMBuffer::PoolEntry *pe = nullptr, Condition c = Always);
|
||||
BufferOffset as_FImm64Pool(VFPRegister dest, double value, Condition c = Always);
|
||||
// load a 32 bit floating point immediate from a pool into a register
|
||||
BufferOffset as_FImm32Pool(VFPRegister dest, float value, ARMBuffer::PoolEntry *pe = nullptr, Condition c = Always);
|
||||
BufferOffset as_FImm32Pool(VFPRegister dest, float value, Condition c = Always);
|
||||
|
||||
// Control flow stuff:
|
||||
|
||||
|
|
|
@ -380,11 +380,11 @@ MacroAssemblerARM::ma_alu(Register src1, Imm32 imm, Register dest,
|
|||
// Going to have to use a load. If the operation is a move, then just move it into the
|
||||
// destination register
|
||||
if (op == op_mov) {
|
||||
as_Imm32Pool(dest, imm.value, nullptr, c);
|
||||
as_Imm32Pool(dest, imm.value, c);
|
||||
return;
|
||||
} else {
|
||||
// If this isn't just going into a register, then stick it in a temp, and then proceed.
|
||||
as_Imm32Pool(ScratchRegister, imm.value, nullptr, c);
|
||||
as_Imm32Pool(ScratchRegister, imm.value, c);
|
||||
}
|
||||
}
|
||||
as_alu(dest, src1, O2Reg(ScratchRegister), op, sc, c);
|
||||
|
@ -442,7 +442,7 @@ MacroAssemblerARM::ma_movPatchable(Imm32 imm_, Register dest, Assembler::Conditi
|
|||
break;
|
||||
case L_LDR:
|
||||
if(i == nullptr)
|
||||
as_Imm32Pool(dest, imm, nullptr, c);
|
||||
as_Imm32Pool(dest, imm, c);
|
||||
else
|
||||
as_WritePoolEntry(i, c, imm);
|
||||
break;
|
||||
|
@ -1332,11 +1332,11 @@ MacroAssemblerARM::ma_b(void *target, Relocation::Kind reloc, Assembler::Conditi
|
|||
as_bx(ScratchRegister, c);
|
||||
break;
|
||||
case Assembler::B_LDR_BX:
|
||||
as_Imm32Pool(ScratchRegister, trg, nullptr, c);
|
||||
as_Imm32Pool(ScratchRegister, trg, c);
|
||||
as_bx(ScratchRegister, c);
|
||||
break;
|
||||
case Assembler::B_LDR:
|
||||
as_Imm32Pool(pc, trg, nullptr, c);
|
||||
as_Imm32Pool(pc, trg, c);
|
||||
if (c == Always)
|
||||
m_buffer.markGuard();
|
||||
break;
|
||||
|
@ -1493,7 +1493,7 @@ MacroAssemblerARM::ma_vimm(double value, FloatRegister dest, Condition cc)
|
|||
}
|
||||
}
|
||||
// Fall back to putting the value in a pool.
|
||||
as_FImm64Pool(dest, value, nullptr, cc);
|
||||
as_FImm64Pool(dest, value, cc);
|
||||
}
|
||||
|
||||
static inline uint32_t
|
||||
|
@ -1530,7 +1530,7 @@ MacroAssemblerARM::ma_vimm_f32(float value, FloatRegister dest, Condition cc)
|
|||
}
|
||||
}
|
||||
// Fall back to putting the value in a pool.
|
||||
as_FImm32Pool(vd, value, nullptr, cc);
|
||||
as_FImm32Pool(vd, value, cc);
|
||||
}
|
||||
|
||||
void
|
||||
|
|
|
@ -5525,7 +5525,10 @@ JS_EncodeStringToUTF8(JSContext *cx, HandleString str)
|
|||
if (!linear)
|
||||
return nullptr;
|
||||
|
||||
return TwoByteCharsToNewUTF8CharsZ(cx, linear->range()).c_str();
|
||||
JS::AutoCheckCannotGC nogc;
|
||||
return linear->hasLatin1Chars()
|
||||
? JS::CharsToNewUTF8CharsZ(cx, linear->latin1Range(nogc)).c_str()
|
||||
: JS::CharsToNewUTF8CharsZ(cx, linear->twoByteRange(nogc)).c_str();
|
||||
}
|
||||
|
||||
JS_PUBLIC_API(size_t)
|
||||
|
|
|
@ -943,23 +943,22 @@ struct EmptySeparatorOp
|
|||
bool operator()(JSContext *, StringBuffer &sb) { return true; }
|
||||
};
|
||||
|
||||
template <typename CharT>
|
||||
struct CharSeparatorOp
|
||||
{
|
||||
jschar sep;
|
||||
explicit CharSeparatorOp(jschar sep) : sep(sep) {};
|
||||
const CharT sep;
|
||||
explicit CharSeparatorOp(CharT sep) : sep(sep) {};
|
||||
bool operator()(JSContext *, StringBuffer &sb) { return sb.append(sep); }
|
||||
};
|
||||
|
||||
struct StringSeparatorOp
|
||||
{
|
||||
const jschar *sepchars;
|
||||
size_t seplen;
|
||||
HandleLinearString sep;
|
||||
|
||||
StringSeparatorOp(const jschar *sepchars, size_t seplen)
|
||||
: sepchars(sepchars), seplen(seplen) {};
|
||||
explicit StringSeparatorOp(HandleLinearString sep) : sep(sep) {}
|
||||
|
||||
bool operator()(JSContext *cx, StringBuffer &sb) {
|
||||
return sb.append(sepchars, seplen);
|
||||
return sb.append(sep);
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -1064,22 +1063,16 @@ ArrayJoin(JSContext *cx, CallArgs &args)
|
|||
return false;
|
||||
|
||||
// Steps 4 and 5
|
||||
RootedString sepstr(cx, nullptr);
|
||||
const jschar *sepchars;
|
||||
size_t seplen;
|
||||
RootedLinearString sepstr(cx);
|
||||
if (!Locale && args.hasDefined(0)) {
|
||||
sepstr = ToString<CanGC>(cx, args[0]);
|
||||
JSString *s = ToString<CanGC>(cx, args[0]);
|
||||
if (!s)
|
||||
return false;
|
||||
sepstr = s->ensureLinear(cx);
|
||||
if (!sepstr)
|
||||
return false;
|
||||
sepchars = sepstr->getChars(cx);
|
||||
if (!sepchars)
|
||||
return false;
|
||||
seplen = sepstr->length();
|
||||
} else {
|
||||
HandlePropertyName comma = cx->names().comma;
|
||||
sepstr = comma;
|
||||
sepchars = comma->chars();
|
||||
seplen = comma->length();
|
||||
sepstr = cx->names().comma;
|
||||
}
|
||||
|
||||
JS::Anchor<JSString*> anchor(sepstr);
|
||||
|
@ -1100,9 +1093,12 @@ ArrayJoin(JSContext *cx, CallArgs &args)
|
|||
}
|
||||
|
||||
StringBuffer sb(cx);
|
||||
if (sepstr->hasTwoByteChars() && !sb.ensureTwoByteChars())
|
||||
return false;
|
||||
|
||||
// The separator will be added |length - 1| times, reserve space for that
|
||||
// so that we don't have to unnecessarily grow the buffer.
|
||||
size_t seplen = sepstr->length();
|
||||
if (length > 0 && !sb.reserve(seplen * (length - 1)))
|
||||
return false;
|
||||
|
||||
|
@ -1112,11 +1108,18 @@ ArrayJoin(JSContext *cx, CallArgs &args)
|
|||
if (!ArrayJoinKernel<Locale>(cx, op, obj, length, sb))
|
||||
return false;
|
||||
} else if (seplen == 1) {
|
||||
CharSeparatorOp op(sepchars[0]);
|
||||
if (!ArrayJoinKernel<Locale>(cx, op, obj, length, sb))
|
||||
return false;
|
||||
jschar c = sepstr->latin1OrTwoByteChar(0);
|
||||
if (c <= JSString::MAX_LATIN1_CHAR) {
|
||||
CharSeparatorOp<Latin1Char> op(c);
|
||||
if (!ArrayJoinKernel<Locale>(cx, op, obj, length, sb))
|
||||
return false;
|
||||
} else {
|
||||
CharSeparatorOp<jschar> op(c);
|
||||
if (!ArrayJoinKernel<Locale>(cx, op, obj, length, sb))
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
StringSeparatorOp op(sepchars, seplen);
|
||||
StringSeparatorOp op(sepstr);
|
||||
if (!ArrayJoinKernel<Locale>(cx, op, obj, length, sb))
|
||||
return false;
|
||||
}
|
||||
|
|
|
@ -1051,6 +1051,10 @@ GCRuntime::GCRuntime(JSRuntime *rt) :
|
|||
systemZone(nullptr),
|
||||
systemAvailableChunkListHead(nullptr),
|
||||
userAvailableChunkListHead(nullptr),
|
||||
#ifdef JSGC_GENERATIONAL
|
||||
nursery(rt),
|
||||
storeBuffer(rt, nursery),
|
||||
#endif
|
||||
bytes(0),
|
||||
maxBytes(0),
|
||||
maxMallocBytes(0),
|
||||
|
@ -1108,10 +1112,6 @@ GCRuntime::GCRuntime(JSRuntime *rt) :
|
|||
objectsMarkedInDeadZones(0),
|
||||
poked(false),
|
||||
heapState(Idle),
|
||||
#ifdef JSGC_GENERATIONAL
|
||||
nursery(rt),
|
||||
storeBuffer(rt, nursery),
|
||||
#endif
|
||||
#ifdef JS_GC_ZEAL
|
||||
zealMode(0),
|
||||
zealFrequency(0),
|
||||
|
|
|
@ -1198,21 +1198,24 @@ Sprinter::putString(JSString *s)
|
|||
InvariantChecker ic(this);
|
||||
|
||||
size_t length = s->length();
|
||||
const jschar *chars = s->getChars(context);
|
||||
if (!chars)
|
||||
return -1;
|
||||
|
||||
size_t size = length;
|
||||
if (size == (size_t) -1)
|
||||
return -1;
|
||||
|
||||
ptrdiff_t oldOffset = offset;
|
||||
char *buffer = reserve(size);
|
||||
if (!buffer)
|
||||
return -1;
|
||||
DeflateStringToBuffer(nullptr, chars, length, buffer, &size);
|
||||
buffer[size] = 0;
|
||||
|
||||
JSLinearString *linear = s->ensureLinear(context);
|
||||
if (!linear)
|
||||
return -1;
|
||||
|
||||
AutoCheckCannotGC nogc;
|
||||
if (linear->hasLatin1Chars())
|
||||
mozilla::PodCopy(reinterpret_cast<Latin1Char*>(buffer), linear->latin1Chars(nogc), length);
|
||||
else
|
||||
DeflateStringToBuffer(nullptr, linear->twoByteChars(nogc), length, buffer, &size);
|
||||
|
||||
buffer[size] = 0;
|
||||
return oldOffset;
|
||||
}
|
||||
|
||||
|
|
|
@ -1317,10 +1317,12 @@ StringMatch(JSLinearString *text, JSLinearString *pat, uint32_t start = 0)
|
|||
static const size_t sRopeMatchThresholdRatioLog2 = 5;
|
||||
|
||||
bool
|
||||
js::StringHasPattern(const jschar *text, uint32_t textLen,
|
||||
const jschar *pat, uint32_t patLen)
|
||||
js::StringHasPattern(JSLinearString *text, const jschar *pat, uint32_t patLen)
|
||||
{
|
||||
return StringMatch(text, textLen, pat, patLen) != -1;
|
||||
AutoCheckCannotGC nogc;
|
||||
return text->hasLatin1Chars()
|
||||
? StringMatch(text->latin1Chars(nogc), text->length(), pat, patLen) != -1
|
||||
: StringMatch(text->twoByteChars(nogc), text->length(), pat, patLen) != -1;
|
||||
}
|
||||
|
||||
int
|
||||
|
@ -3704,13 +3706,11 @@ class SplitStringMatcher
|
|||
bool operator()(JSContext *cx, JSLinearString *str, size_t index, SplitMatchResult *res) const
|
||||
{
|
||||
JS_ASSERT(index == 0 || index < str->length());
|
||||
const jschar *chars = str->chars();
|
||||
int match = StringMatch(chars + index, str->length() - index,
|
||||
sep->chars(), sep->length());
|
||||
int match = StringMatch(str, sep, index);
|
||||
if (match == -1)
|
||||
res->setFailure();
|
||||
else
|
||||
res->setResult(sep->length(), index + match + sep->length());
|
||||
res->setResult(sep->length(), match + sep->length());
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
@ -4464,8 +4464,8 @@ js::StringToSource(JSContext *cx, JSString *str)
|
|||
return js_QuoteString(cx, str, '"');
|
||||
}
|
||||
|
||||
static bool
|
||||
EqualChars(JSLinearString *str1, JSLinearString *str2)
|
||||
bool
|
||||
js::EqualChars(JSLinearString *str1, JSLinearString *str2)
|
||||
{
|
||||
MOZ_ASSERT(str1->length() == str2->length());
|
||||
|
||||
|
@ -4523,8 +4523,28 @@ js::EqualStrings(JSLinearString *str1, JSLinearString *str2)
|
|||
return EqualChars(str1, str2);
|
||||
}
|
||||
|
||||
static bool
|
||||
CompareStringsImpl(JSContext *cx, JSString *str1, JSString *str2, int32_t *result)
|
||||
static int32_t
|
||||
CompareStringsImpl(JSLinearString *str1, JSLinearString *str2)
|
||||
{
|
||||
size_t len1 = str1->length();
|
||||
size_t len2 = str2->length();
|
||||
|
||||
AutoCheckCannotGC nogc;
|
||||
if (str1->hasLatin1Chars()) {
|
||||
const Latin1Char *chars1 = str1->latin1Chars(nogc);
|
||||
return str2->hasLatin1Chars()
|
||||
? CompareChars(chars1, len1, str2->latin1Chars(nogc), len2)
|
||||
: CompareChars(chars1, len1, str2->twoByteChars(nogc), len2);
|
||||
}
|
||||
|
||||
const jschar *chars1 = str1->twoByteChars(nogc);
|
||||
return str2->hasLatin1Chars()
|
||||
? CompareChars(chars1, len1, str2->latin1Chars(nogc), len2)
|
||||
: CompareChars(chars1, len1, str2->twoByteChars(nogc), len2);
|
||||
}
|
||||
|
||||
bool
|
||||
js::CompareStrings(JSContext *cx, JSString *str1, JSString *str2, int32_t *result)
|
||||
{
|
||||
JS_ASSERT(str1);
|
||||
JS_ASSERT(str2);
|
||||
|
@ -4534,28 +4554,22 @@ CompareStringsImpl(JSContext *cx, JSString *str1, JSString *str2, int32_t *resul
|
|||
return true;
|
||||
}
|
||||
|
||||
const jschar *s1 = str1->getChars(cx);
|
||||
if (!s1)
|
||||
JSLinearString *linear1 = str1->ensureLinear(cx);
|
||||
if (!linear1)
|
||||
return false;
|
||||
|
||||
const jschar *s2 = str2->getChars(cx);
|
||||
if (!s2)
|
||||
JSLinearString *linear2 = str2->ensureLinear(cx);
|
||||
if (!linear2)
|
||||
return false;
|
||||
|
||||
*result = CompareChars(s1, str1->length(), s2, str2->length());
|
||||
*result = CompareStringsImpl(linear1, linear2);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
js::CompareStrings(JSContext *cx, JSString *str1, JSString *str2, int32_t *result)
|
||||
{
|
||||
return CompareStringsImpl(cx, str1, str2, result);
|
||||
}
|
||||
|
||||
int32_t
|
||||
js::CompareAtoms(JSAtom *atom1, JSAtom *atom2)
|
||||
{
|
||||
return CompareChars(atom1->chars(), atom1->length(), atom2->chars(), atom2->length());
|
||||
return CompareStringsImpl(atom1, atom2);
|
||||
}
|
||||
|
||||
bool
|
||||
|
@ -4568,12 +4582,13 @@ js::StringEqualsAscii(JSLinearString *str, const char *asciiBytes)
|
|||
#endif
|
||||
if (length != str->length())
|
||||
return false;
|
||||
const jschar *chars = str->chars();
|
||||
for (size_t i = 0; i != length; ++i) {
|
||||
if (unsigned(asciiBytes[i]) != unsigned(chars[i]))
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
|
||||
const Latin1Char *latin1 = reinterpret_cast<const Latin1Char *>(asciiBytes);
|
||||
|
||||
AutoCheckCannotGC nogc;
|
||||
return str->hasLatin1Chars()
|
||||
? PodEqual(latin1, str->latin1Chars(nogc), length)
|
||||
: EqualCharsLatin1TwoByte(latin1, str->twoByteChars(nogc), length);
|
||||
}
|
||||
|
||||
size_t
|
||||
|
|
|
@ -48,16 +48,17 @@ SkipSpace(const CharT *s, const CharT *end)
|
|||
|
||||
// Return less than, equal to, or greater than zero depending on whether
|
||||
// s1 is less than, equal to, or greater than s2.
|
||||
template <typename Char1, typename Char2>
|
||||
inline int32_t
|
||||
CompareChars(const jschar *s1, size_t l1, const jschar *s2, size_t l2)
|
||||
CompareChars(const Char1 *s1, size_t len1, const Char2 *s2, size_t len2)
|
||||
{
|
||||
size_t n = Min(l1, l2);
|
||||
size_t n = Min(len1, len2);
|
||||
for (size_t i = 0; i < n; i++) {
|
||||
if (int32_t cmp = s1[i] - s2[i])
|
||||
return cmp;
|
||||
}
|
||||
|
||||
return (int32_t)(l1 - l2);
|
||||
return int32_t(len1 - len2);
|
||||
}
|
||||
|
||||
} /* namespace js */
|
||||
|
@ -183,6 +184,9 @@ EqualStrings(JSContext *cx, JSLinearString *str1, JSLinearString *str2, bool *re
|
|||
extern bool
|
||||
EqualStrings(JSLinearString *str1, JSLinearString *str2);
|
||||
|
||||
extern bool
|
||||
EqualChars(JSLinearString *str1, JSLinearString *str2);
|
||||
|
||||
/*
|
||||
* Return less than, equal to, or greater than zero depending on whether
|
||||
* str1 is less than, equal to, or greater than str2.
|
||||
|
@ -201,8 +205,7 @@ StringEqualsAscii(JSLinearString *str, const char *asciiBytes);
|
|||
|
||||
/* Return true if the string contains a pattern anywhere inside it. */
|
||||
extern bool
|
||||
StringHasPattern(const jschar *text, uint32_t textlen,
|
||||
const jschar *pat, uint32_t patlen);
|
||||
StringHasPattern(JSLinearString *text, const jschar *pat, uint32_t patlen);
|
||||
|
||||
extern int
|
||||
StringFindPattern(const jschar *text, uint32_t textlen,
|
||||
|
|
|
@ -320,16 +320,6 @@ GetLine(FILE *file, const char * prompt)
|
|||
return nullptr;
|
||||
}
|
||||
|
||||
static char *
|
||||
JSStringToUTF8(JSContext *cx, JSString *str)
|
||||
{
|
||||
JSLinearString *linear = str->ensureLinear(cx);
|
||||
if (!linear)
|
||||
return nullptr;
|
||||
|
||||
return TwoByteCharsToNewUTF8CharsZ(cx, linear->range()).c_str();
|
||||
}
|
||||
|
||||
/* State to store as JSContext private. */
|
||||
struct JSShellContextData {
|
||||
/* Creation timestamp, used by the elapsed() shell builtin. */
|
||||
|
@ -492,7 +482,7 @@ EvalAndPrint(JSContext *cx, Handle<JSObject*> global, const char *bytes, size_t
|
|||
if (!str)
|
||||
return false;
|
||||
|
||||
char *utf8chars = JSStringToUTF8(cx, str);
|
||||
char *utf8chars = JS_EncodeStringToUTF8(cx, str);
|
||||
if (!utf8chars)
|
||||
return false;
|
||||
fprintf(out, "%s\n", utf8chars);
|
||||
|
@ -1446,14 +1436,17 @@ Run(JSContext *cx, unsigned argc, jsval *vp)
|
|||
if (!filename)
|
||||
return false;
|
||||
|
||||
const jschar *ucbuf = nullptr;
|
||||
size_t buflen;
|
||||
str = FileAsString(cx, filename.ptr());
|
||||
if (str)
|
||||
ucbuf = JS_GetStringCharsAndLength(cx, str, &buflen);
|
||||
if (!ucbuf)
|
||||
if (!str)
|
||||
return false;
|
||||
|
||||
AutoStableStringChars chars(cx, &str->asLinear());
|
||||
if (!chars.initTwoByte(cx))
|
||||
return false;
|
||||
|
||||
const jschar *ucbuf = chars.twoByteRange().start().get();
|
||||
size_t buflen = str->length();
|
||||
|
||||
JS::Anchor<JSString *> a_str(str);
|
||||
|
||||
RootedScript script(cx);
|
||||
|
@ -1564,10 +1557,10 @@ PutStr(JSContext *cx, unsigned argc, jsval *vp)
|
|||
CallArgs args = CallArgsFromVp(argc, vp);
|
||||
|
||||
if (args.length() != 0) {
|
||||
JSString *str = JS::ToString(cx, args[0]);
|
||||
RootedString str(cx, JS::ToString(cx, args[0]));
|
||||
if (!str)
|
||||
return false;
|
||||
char *bytes = JSStringToUTF8(cx, str);
|
||||
char *bytes = JS_EncodeStringToUTF8(cx, str);
|
||||
if (!bytes)
|
||||
return false;
|
||||
fputs(bytes, gOutFile);
|
||||
|
@ -1592,10 +1585,10 @@ static bool
|
|||
PrintInternal(JSContext *cx, const CallArgs &args, FILE *file)
|
||||
{
|
||||
for (unsigned i = 0; i < args.length(); i++) {
|
||||
JSString *str = JS::ToString(cx, args[i]);
|
||||
RootedString str(cx, JS::ToString(cx, args[i]));
|
||||
if (!str)
|
||||
return false;
|
||||
char *bytes = JSStringToUTF8(cx, str);
|
||||
char *bytes = JS_EncodeStringToUTF8(cx, str);
|
||||
if (!bytes)
|
||||
return false;
|
||||
fprintf(file, "%s%s", i ? " " : "", bytes);
|
||||
|
|
|
@ -156,7 +156,7 @@ struct CopyScriptFrameIterArgs
|
|||
* invalid.
|
||||
*/
|
||||
void maybeForwardToCallObject(JSObject *obj, ArgumentsData *data) {
|
||||
if (!iter_.isJit())
|
||||
if (!iter_.isIon())
|
||||
ArgumentsObject::MaybeForwardToCallObject(iter_.abstractFramePtr(), obj, data);
|
||||
}
|
||||
};
|
||||
|
|
|
@ -25,16 +25,13 @@ JS::LossyTwoByteCharsToNewLatin1CharsZ(js::ThreadSafeContext *cx, TwoByteChars t
|
|||
return Latin1CharsZ(latin1, len);
|
||||
}
|
||||
|
||||
template <typename CharT>
|
||||
static size_t
|
||||
GetDeflatedUTF8StringLength(const jschar *chars, size_t nchars)
|
||||
GetDeflatedUTF8StringLength(const CharT *chars, size_t nchars)
|
||||
{
|
||||
size_t nbytes;
|
||||
const jschar *end;
|
||||
unsigned c, c2;
|
||||
|
||||
nbytes = nchars;
|
||||
for (end = chars + nchars; chars != end; chars++) {
|
||||
c = *chars;
|
||||
size_t nbytes = nchars;
|
||||
for (const CharT *end = chars + nchars; chars < end; chars++) {
|
||||
jschar c = *chars;
|
||||
if (c < 0x80)
|
||||
continue;
|
||||
if (0xD800 <= c && c <= 0xDFFF) {
|
||||
|
@ -43,7 +40,7 @@ GetDeflatedUTF8StringLength(const jschar *chars, size_t nchars)
|
|||
nbytes += 2; /* Bad Surrogate */
|
||||
continue;
|
||||
}
|
||||
c2 = chars[1];
|
||||
jschar c2 = chars[1];
|
||||
if (c2 < 0xDC00 || c2 > 0xDFFF) {
|
||||
nbytes += 2; /* Bad Surrogate */
|
||||
continue;
|
||||
|
@ -77,8 +74,9 @@ PutUTF8ReplacementCharacter(char **dst, size_t *dstlenp) {
|
|||
* Write up to |*dstlenp| bytes into |dst|. Writes the number of bytes used
|
||||
* into |*dstlenp| on success. Returns false on failure.
|
||||
*/
|
||||
template <typename CharT>
|
||||
static bool
|
||||
DeflateStringToUTF8Buffer(js::ThreadSafeContext *cx, const jschar *src, size_t srclen,
|
||||
DeflateStringToUTF8Buffer(js::ThreadSafeContext *cx, const CharT *src, size_t srclen,
|
||||
char *dst, size_t *dstlenp)
|
||||
{
|
||||
size_t dstlen = *dstlenp;
|
||||
|
@ -140,28 +138,34 @@ bufferTooSmall:
|
|||
return false;
|
||||
}
|
||||
|
||||
|
||||
template <typename CharT>
|
||||
UTF8CharsZ
|
||||
JS::TwoByteCharsToNewUTF8CharsZ(js::ThreadSafeContext *cx, TwoByteChars tbchars)
|
||||
JS::CharsToNewUTF8CharsZ(js::ThreadSafeContext *cx, const mozilla::Range<const CharT> chars)
|
||||
{
|
||||
JS_ASSERT(cx);
|
||||
|
||||
/* Get required buffer size. */
|
||||
jschar *str = tbchars.start().get();
|
||||
size_t len = GetDeflatedUTF8StringLength(str, tbchars.length());
|
||||
const CharT *str = chars.start().get();
|
||||
size_t len = GetDeflatedUTF8StringLength(str, chars.length());
|
||||
|
||||
/* Allocate buffer. */
|
||||
unsigned char *utf8 = cx->pod_malloc<unsigned char>(len + 1);
|
||||
char *utf8 = cx->pod_malloc<char>(len + 1);
|
||||
if (!utf8)
|
||||
return UTF8CharsZ();
|
||||
|
||||
/* Encode to UTF8. */
|
||||
DeflateStringToUTF8Buffer(cx, str, tbchars.length(), (char *)utf8, &len);
|
||||
DeflateStringToUTF8Buffer(cx, str, chars.length(), utf8, &len);
|
||||
utf8[len] = '\0';
|
||||
|
||||
return UTF8CharsZ(utf8, len);
|
||||
}
|
||||
|
||||
template UTF8CharsZ
|
||||
JS::CharsToNewUTF8CharsZ(js::ThreadSafeContext *cx, mozilla::Range<const Latin1Char> chars);
|
||||
|
||||
template UTF8CharsZ
|
||||
JS::CharsToNewUTF8CharsZ(js::ThreadSafeContext *cx, mozilla::Range<const jschar> chars);
|
||||
|
||||
static const uint32_t INVALID_UTF8 = UINT32_MAX;
|
||||
|
||||
/*
|
||||
|
|
|
@ -292,7 +292,7 @@ intrinsic_ParallelSpew(ThreadSafeContext *cx, unsigned argc, Value *vp)
|
|||
if (!inspector.ensureChars(cx, nogc))
|
||||
return false;
|
||||
|
||||
ScopedJSFreePtr<char> bytes(TwoByteCharsToNewUTF8CharsZ(cx, inspector.twoByteRange()).c_str());
|
||||
ScopedJSFreePtr<char> bytes(JS::CharsToNewUTF8CharsZ(cx, inspector.twoByteRange()).c_str());
|
||||
parallel::Spew(parallel::SpewOps, bytes);
|
||||
|
||||
args.rval().setUndefined();
|
||||
|
|
|
@ -320,7 +320,7 @@ JSRope::flattenInternal(ExclusiveContext *maybecx)
|
|||
}
|
||||
JSString *child = str->d.s.u2.left;
|
||||
JS_ASSERT(child->isRope());
|
||||
str->d.s.u2.nonInlineCharsTwoByte = left.nonInlineChars();
|
||||
str->setNonInlineChars(left.nonInlineChars<CharT>(nogc));
|
||||
child->d.u1.flattenData = uintptr_t(str) | Tag_VisitRightChild;
|
||||
str = child;
|
||||
}
|
||||
|
|
|
@ -258,6 +258,8 @@ class JSString : public js::gc::BarrieredCell<JSString>
|
|||
|
||||
static const uint32_t MAX_LENGTH = JS_BIT(28) - 1;
|
||||
|
||||
static const JS::Latin1Char MAX_LATIN1_CHAR = 0xff;
|
||||
|
||||
/*
|
||||
* Helper function to validate that a string of a given length is
|
||||
* representable by a JSString. An allocation overflow is reported if false
|
||||
|
@ -655,14 +657,14 @@ class JSLinearString : public JSString
|
|||
return JS::TwoByteChars(chars(), length());
|
||||
}
|
||||
|
||||
JS::Latin1Chars latin1Range(const JS::AutoCheckCannotGC &nogc) const {
|
||||
mozilla::Range<const JS::Latin1Char> latin1Range(const JS::AutoCheckCannotGC &nogc) const {
|
||||
JS_ASSERT(JSString::isLinear());
|
||||
return JS::Latin1Chars(latin1Chars(nogc), length());
|
||||
return mozilla::Range<const JS::Latin1Char>(latin1Chars(nogc), length());
|
||||
}
|
||||
|
||||
JS::TwoByteChars twoByteRange(const JS::AutoCheckCannotGC &nogc) const {
|
||||
mozilla::Range<const jschar> twoByteRange(const JS::AutoCheckCannotGC &nogc) const {
|
||||
JS_ASSERT(JSString::isLinear());
|
||||
return JS::TwoByteChars(twoByteChars(nogc), length());
|
||||
return mozilla::Range<const jschar>(twoByteChars(nogc), length());
|
||||
}
|
||||
|
||||
MOZ_ALWAYS_INLINE
|
||||
|
@ -1046,9 +1048,13 @@ class ScopedThreadSafeStringInspector
|
|||
return latin1Chars_;
|
||||
}
|
||||
|
||||
JS::TwoByteChars twoByteRange() const {
|
||||
mozilla::Range<const Latin1Char> latin1Range() const {
|
||||
MOZ_ASSERT(state_ == Latin1);
|
||||
return mozilla::Range<const Latin1Char>(latin1Chars_, str_->length());
|
||||
}
|
||||
mozilla::Range<const jschar> twoByteRange() const {
|
||||
MOZ_ASSERT(state_ == TwoByte);
|
||||
return JS::TwoByteChars(twoByteChars_, str_->length());
|
||||
return mozilla::Range<const jschar>(twoByteChars_, str_->length());
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -1064,7 +1070,7 @@ class ScopedThreadSafeStringInspector
|
|||
class MOZ_STACK_CLASS AutoStableStringChars
|
||||
{
|
||||
/* Ensure the string is kept alive while we're using its chars. */
|
||||
Rooted<JSLinearString*> s_;
|
||||
RootedLinearString s_;
|
||||
union {
|
||||
const jschar *twoByteChars_;
|
||||
const JS::Latin1Char *latin1Chars_;
|
||||
|
@ -1097,6 +1103,16 @@ class MOZ_STACK_CLASS AutoStableStringChars
|
|||
return mozilla::Range<const jschar>(twoByteChars_, s_->length());
|
||||
}
|
||||
|
||||
/* If we own the chars, transfer ownership to the caller. */
|
||||
bool maybeGiveOwnershipToCaller() {
|
||||
MOZ_ASSERT(state_ != Uninitialized);
|
||||
if (!ownsChars_)
|
||||
return false;
|
||||
state_ = Uninitialized;
|
||||
ownsChars_ = false;
|
||||
return true;
|
||||
}
|
||||
|
||||
private:
|
||||
AutoStableStringChars(const AutoStableStringChars &other) MOZ_DELETE;
|
||||
void operator=(const AutoStableStringChars &other) MOZ_DELETE;
|
||||
|
|
|
@ -67,8 +67,6 @@ class StringBuffer
|
|||
return cb.ref<TwoByteCharBuffer>();
|
||||
}
|
||||
|
||||
static const Latin1Char MaxLatin1Char = 0xff;
|
||||
|
||||
bool inflateChars();
|
||||
|
||||
public:
|
||||
|
@ -107,7 +105,7 @@ class StringBuffer
|
|||
|
||||
inline bool append(const jschar c) {
|
||||
if (isLatin1()) {
|
||||
if (c <= MaxLatin1Char)
|
||||
if (c <= JSString::MAX_LATIN1_CHAR)
|
||||
return latin1Chars().append(Latin1Char(c));
|
||||
if (!inflateChars())
|
||||
return false;
|
||||
|
@ -224,7 +222,7 @@ StringBuffer::append(const jschar *begin, const jschar *end)
|
|||
while (true) {
|
||||
if (begin >= end)
|
||||
return true;
|
||||
if (*begin > MaxLatin1Char)
|
||||
if (*begin > JSString::MAX_LATIN1_CHAR)
|
||||
break;
|
||||
if (!latin1Chars().append(*begin))
|
||||
return false;
|
||||
|
@ -246,7 +244,9 @@ StringBuffer::append(JSLinearString *str)
|
|||
if (!inflateChars())
|
||||
return false;
|
||||
}
|
||||
return twoByteChars().append(str->twoByteChars(nogc), str->length());
|
||||
return str->hasLatin1Chars()
|
||||
? twoByteChars().append(str->latin1Chars(nogc), str->length())
|
||||
: twoByteChars().append(str->twoByteChars(nogc), str->length());
|
||||
}
|
||||
|
||||
inline bool
|
||||
|
@ -261,7 +261,9 @@ StringBuffer::appendSubstring(JSLinearString *base, size_t off, size_t len)
|
|||
if (!inflateChars())
|
||||
return false;
|
||||
}
|
||||
return twoByteChars().append(base->twoByteChars(nogc) + off, len);
|
||||
return base->hasLatin1Chars()
|
||||
? twoByteChars().append(base->latin1Chars(nogc) + off, len)
|
||||
: twoByteChars().append(base->twoByteChars(nogc) + off, len);
|
||||
}
|
||||
|
||||
inline bool
|
||||
|
|
|
@ -15,7 +15,7 @@
|
|||
#include "nsStringFwd.h"
|
||||
|
||||
class nsPresState;
|
||||
template<typename> class already_AddRefed;
|
||||
template<typename> struct already_AddRefed;
|
||||
|
||||
#define NS_ILAYOUTHISTORYSTATE_IID \
|
||||
{ 0x5208993e, 0xd812, 0x431e, \
|
||||
|
|
|
@ -4595,17 +4595,19 @@ nsLayoutUtils::GetCenteredFontBaseline(nsFontMetrics* aFontMetrics,
|
|||
|
||||
|
||||
/* static */ bool
|
||||
nsLayoutUtils::GetFirstLineBaseline(const nsIFrame* aFrame, nscoord* aResult)
|
||||
nsLayoutUtils::GetFirstLineBaseline(WritingMode aWritingMode,
|
||||
const nsIFrame* aFrame, nscoord* aResult)
|
||||
{
|
||||
LinePosition position;
|
||||
if (!GetFirstLinePosition(aFrame, &position))
|
||||
if (!GetFirstLinePosition(aWritingMode, aFrame, &position))
|
||||
return false;
|
||||
*aResult = position.mBaseline;
|
||||
return true;
|
||||
}
|
||||
|
||||
/* static */ bool
|
||||
nsLayoutUtils::GetFirstLinePosition(const nsIFrame* aFrame,
|
||||
nsLayoutUtils::GetFirstLinePosition(WritingMode aWM,
|
||||
const nsIFrame* aFrame,
|
||||
LinePosition* aResult)
|
||||
{
|
||||
const nsBlockFrame* block = nsLayoutUtils::GetAsBlock(const_cast<nsIFrame*>(aFrame));
|
||||
|
@ -4614,11 +4616,11 @@ nsLayoutUtils::GetFirstLinePosition(const nsIFrame* aFrame,
|
|||
// so, use the baseline of its first row.
|
||||
nsIAtom* fType = aFrame->GetType();
|
||||
if (fType == nsGkAtoms::tableOuterFrame) {
|
||||
aResult->mTop = 0;
|
||||
aResult->mBaseline = aFrame->GetBaseline();
|
||||
aResult->mBStart = 0;
|
||||
aResult->mBaseline = aFrame->GetLogicalBaseline(aWM);
|
||||
// This is what we want for the list bullet caller; not sure if
|
||||
// other future callers will want the same.
|
||||
aResult->mBottom = aFrame->GetSize().height;
|
||||
aResult->mBEnd = aFrame->BSize(aWM);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -4629,11 +4631,13 @@ nsLayoutUtils::GetFirstLinePosition(const nsIFrame* aFrame,
|
|||
NS_NOTREACHED("not scroll frame");
|
||||
}
|
||||
LinePosition kidPosition;
|
||||
if (GetFirstLinePosition(sFrame->GetScrolledFrame(), &kidPosition)) {
|
||||
if (GetFirstLinePosition(aWM,
|
||||
sFrame->GetScrolledFrame(), &kidPosition)) {
|
||||
// Consider only the border and padding that contributes to the
|
||||
// kid's position, not the scrolling, so we get the initial
|
||||
// position.
|
||||
*aResult = kidPosition + aFrame->GetUsedBorderAndPadding().top;
|
||||
*aResult = kidPosition +
|
||||
aFrame->GetLogicalUsedBorderAndPadding(aWM).BStart(aWM);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
|
@ -4643,8 +4647,9 @@ nsLayoutUtils::GetFirstLinePosition(const nsIFrame* aFrame,
|
|||
LinePosition kidPosition;
|
||||
nsIFrame* kid = aFrame->GetFirstPrincipalChild();
|
||||
// kid might be a legend frame here, but that's ok.
|
||||
if (GetFirstLinePosition(kid, &kidPosition)) {
|
||||
*aResult = kidPosition + kid->GetNormalPosition().y;
|
||||
if (GetFirstLinePosition(aWM, kid, &kidPosition)) {
|
||||
*aResult = kidPosition +
|
||||
kid->GetLogicalNormalPosition(aWM, aFrame->GetSize().width).B(aWM);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
|
@ -4660,18 +4665,23 @@ nsLayoutUtils::GetFirstLinePosition(const nsIFrame* aFrame,
|
|||
if (line->IsBlock()) {
|
||||
nsIFrame *kid = line->mFirstChild;
|
||||
LinePosition kidPosition;
|
||||
if (GetFirstLinePosition(kid, &kidPosition)) {
|
||||
*aResult = kidPosition + kid->GetNormalPosition().y;
|
||||
if (GetFirstLinePosition(aWM, kid, &kidPosition)) {
|
||||
//XXX Not sure if this is the correct value to use for container
|
||||
// width here. It will only be used in vertical-rl layout,
|
||||
// which we don't have full support and testing for yet.
|
||||
nscoord containerWidth = line->mContainerWidth;
|
||||
*aResult = kidPosition +
|
||||
kid->GetLogicalNormalPosition(aWM, containerWidth).B(aWM);
|
||||
return true;
|
||||
}
|
||||
} else {
|
||||
// XXX Is this the right test? We have some bogus empty lines
|
||||
// floating around, but IsEmpty is perhaps too weak.
|
||||
if (line->BSize() != 0 || !line->IsEmpty()) {
|
||||
nscoord top = line->BStart();
|
||||
aResult->mTop = top;
|
||||
aResult->mBaseline = top + line->GetAscent();
|
||||
aResult->mBottom = top + line->BSize();
|
||||
nscoord bStart = line->BStart();
|
||||
aResult->mBStart = bStart;
|
||||
aResult->mBaseline = bStart + line->GetLogicalAscent();
|
||||
aResult->mBEnd = bStart + line->BSize();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
@ -4680,7 +4690,8 @@ nsLayoutUtils::GetFirstLinePosition(const nsIFrame* aFrame,
|
|||
}
|
||||
|
||||
/* static */ bool
|
||||
nsLayoutUtils::GetLastLineBaseline(const nsIFrame* aFrame, nscoord* aResult)
|
||||
nsLayoutUtils::GetLastLineBaseline(WritingMode aWM,
|
||||
const nsIFrame* aFrame, nscoord* aResult)
|
||||
{
|
||||
const nsBlockFrame* block = nsLayoutUtils::GetAsBlock(const_cast<nsIFrame*>(aFrame));
|
||||
if (!block)
|
||||
|
@ -4693,21 +4704,24 @@ nsLayoutUtils::GetLastLineBaseline(const nsIFrame* aFrame, nscoord* aResult)
|
|||
if (line->IsBlock()) {
|
||||
nsIFrame *kid = line->mFirstChild;
|
||||
nscoord kidBaseline;
|
||||
if (GetLastLineBaseline(kid, &kidBaseline)) {
|
||||
nscoord containerWidth = line->mContainerWidth;
|
||||
if (GetLastLineBaseline(aWM, kid, &kidBaseline)) {
|
||||
// Ignore relative positioning for baseline calculations
|
||||
*aResult = kidBaseline + kid->GetNormalPosition().y;
|
||||
*aResult = kidBaseline +
|
||||
kid->GetLogicalNormalPosition(aWM, containerWidth).B(aWM);
|
||||
return true;
|
||||
} else if (kid->GetType() == nsGkAtoms::scrollFrame) {
|
||||
// Use the bottom of the scroll frame.
|
||||
// XXX CSS2.1 really doesn't say what to do here.
|
||||
*aResult = kid->GetNormalPosition().y + kid->GetRect().height;
|
||||
*aResult = kid->GetLogicalNormalPosition(aWM, containerWidth).B(aWM) +
|
||||
kid->BSize(aWM);
|
||||
return true;
|
||||
}
|
||||
} else {
|
||||
// XXX Is this the right test? We have some bogus empty lines
|
||||
// floating around, but IsEmpty is perhaps too weak.
|
||||
if (line->BSize() != 0 || !line->IsEmpty()) {
|
||||
*aResult = line->BStart() + line->GetAscent();
|
||||
*aResult = line->BStart() + line->GetLogicalAscent();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
@ -4716,45 +4730,49 @@ nsLayoutUtils::GetLastLineBaseline(const nsIFrame* aFrame, nscoord* aResult)
|
|||
}
|
||||
|
||||
static nscoord
|
||||
CalculateBlockContentBottom(nsBlockFrame* aFrame)
|
||||
CalculateBlockContentBEnd(WritingMode aWM, nsBlockFrame* aFrame)
|
||||
{
|
||||
NS_PRECONDITION(aFrame, "null ptr");
|
||||
|
||||
nscoord contentBottom = 0;
|
||||
nscoord contentBEnd = 0;
|
||||
|
||||
for (nsBlockFrame::line_iterator line = aFrame->begin_lines(),
|
||||
line_end = aFrame->end_lines();
|
||||
line != line_end; ++line) {
|
||||
if (line->IsBlock()) {
|
||||
nsIFrame* child = line->mFirstChild;
|
||||
nscoord offset = child->GetNormalPosition().y;
|
||||
contentBottom = std::max(contentBottom,
|
||||
nsLayoutUtils::CalculateContentBottom(child) + offset);
|
||||
nscoord containerWidth = line->mContainerWidth;
|
||||
nscoord offset =
|
||||
child->GetLogicalNormalPosition(aWM, containerWidth).B(aWM);
|
||||
contentBEnd =
|
||||
std::max(contentBEnd,
|
||||
nsLayoutUtils::CalculateContentBEnd(aWM, child) + offset);
|
||||
}
|
||||
else {
|
||||
contentBottom = std::max(contentBottom, line->BEnd());
|
||||
contentBEnd = std::max(contentBEnd, line->BEnd());
|
||||
}
|
||||
}
|
||||
return contentBottom;
|
||||
return contentBEnd;
|
||||
}
|
||||
|
||||
/* static */ nscoord
|
||||
nsLayoutUtils::CalculateContentBottom(nsIFrame* aFrame)
|
||||
nsLayoutUtils::CalculateContentBEnd(WritingMode aWM, nsIFrame* aFrame)
|
||||
{
|
||||
NS_PRECONDITION(aFrame, "null ptr");
|
||||
|
||||
nscoord contentBottom = aFrame->GetRect().height;
|
||||
nscoord contentBEnd = aFrame->BSize(aWM);
|
||||
|
||||
// We want scrollable overflow rather than visual because this
|
||||
// calculation is intended to affect layout.
|
||||
if (aFrame->GetScrollableOverflowRect().height > contentBottom) {
|
||||
LogicalSize overflowSize(aWM, aFrame->GetScrollableOverflowRect().Size());
|
||||
if (overflowSize.BSize(aWM) > contentBEnd) {
|
||||
nsIFrame::ChildListIDs skip(nsIFrame::kOverflowList |
|
||||
nsIFrame::kExcessOverflowContainersList |
|
||||
nsIFrame::kOverflowOutOfFlowList);
|
||||
nsBlockFrame* blockFrame = GetAsBlock(aFrame);
|
||||
if (blockFrame) {
|
||||
contentBottom =
|
||||
std::max(contentBottom, CalculateBlockContentBottom(blockFrame));
|
||||
contentBEnd =
|
||||
std::max(contentBEnd, CalculateBlockContentBEnd(aWM, blockFrame));
|
||||
skip |= nsIFrame::kPrincipalList;
|
||||
}
|
||||
nsIFrame::ChildListIterator lists(aFrame);
|
||||
|
@ -4763,14 +4781,16 @@ nsLayoutUtils::CalculateContentBottom(nsIFrame* aFrame)
|
|||
nsFrameList::Enumerator childFrames(lists.CurrentList());
|
||||
for (; !childFrames.AtEnd(); childFrames.Next()) {
|
||||
nsIFrame* child = childFrames.get();
|
||||
nscoord offset = child->GetNormalPosition().y;
|
||||
contentBottom = std::max(contentBottom,
|
||||
CalculateContentBottom(child) + offset);
|
||||
nscoord offset =
|
||||
child->GetLogicalNormalPosition(aWM,
|
||||
aFrame->GetSize().width).B(aWM);
|
||||
contentBEnd = std::max(contentBEnd,
|
||||
CalculateContentBEnd(aWM, child) + offset);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return contentBottom;
|
||||
return contentBEnd;
|
||||
}
|
||||
|
||||
/* static */ nsIFrame*
|
||||
|
|
|
@ -65,6 +65,7 @@ namespace mozilla {
|
|||
class SVGImageContext;
|
||||
struct IntrinsicSize;
|
||||
struct ContainerLayerParameters;
|
||||
class WritingMode;
|
||||
namespace dom {
|
||||
class DOMRectList;
|
||||
class Element;
|
||||
|
@ -1360,7 +1361,8 @@ public:
|
|||
* Returns true if a baseline was found (and fills in aResult).
|
||||
* Otherwise returns false.
|
||||
*/
|
||||
static bool GetFirstLineBaseline(const nsIFrame* aFrame, nscoord* aResult);
|
||||
static bool GetFirstLineBaseline(mozilla::WritingMode aWritingMode,
|
||||
const nsIFrame* aFrame, nscoord* aResult);
|
||||
|
||||
/**
|
||||
* Just like GetFirstLineBaseline, except also returns the top and
|
||||
|
@ -1370,17 +1372,18 @@ public:
|
|||
* Otherwise returns false.
|
||||
*/
|
||||
struct LinePosition {
|
||||
nscoord mTop, mBaseline, mBottom;
|
||||
nscoord mBStart, mBaseline, mBEnd;
|
||||
|
||||
LinePosition operator+(nscoord aOffset) const {
|
||||
LinePosition result;
|
||||
result.mTop = mTop + aOffset;
|
||||
result.mBStart = mBStart + aOffset;
|
||||
result.mBaseline = mBaseline + aOffset;
|
||||
result.mBottom = mBottom + aOffset;
|
||||
result.mBEnd = mBEnd + aOffset;
|
||||
return result;
|
||||
}
|
||||
};
|
||||
static bool GetFirstLinePosition(const nsIFrame* aFrame,
|
||||
static bool GetFirstLinePosition(mozilla::WritingMode aWritingMode,
|
||||
const nsIFrame* aFrame,
|
||||
LinePosition* aResult);
|
||||
|
||||
|
||||
|
@ -1392,17 +1395,20 @@ public:
|
|||
* Returns true if a baseline was found (and fills in aResult).
|
||||
* Otherwise returns false.
|
||||
*/
|
||||
static bool GetLastLineBaseline(const nsIFrame* aFrame, nscoord* aResult);
|
||||
static bool GetLastLineBaseline(mozilla::WritingMode aWritingMode,
|
||||
const nsIFrame* aFrame, nscoord* aResult);
|
||||
|
||||
/**
|
||||
* Returns a y coordinate relative to this frame's origin that represents
|
||||
* the logical bottom of the frame or its visible content, whichever is lower.
|
||||
* Returns a block-dir coordinate relative to this frame's origin that
|
||||
* represents the logical block-end of the frame or its visible content,
|
||||
* whichever is further from the origin.
|
||||
* Relative positioning is ignored and margins and glyph bounds are not
|
||||
* considered.
|
||||
* This value will be >= mRect.height() and <= overflowRect.YMost() unless
|
||||
* This value will be >= mRect.BSize() and <= overflowRect.BEnd() unless
|
||||
* relative positioning is applied.
|
||||
*/
|
||||
static nscoord CalculateContentBottom(nsIFrame* aFrame);
|
||||
static nscoord CalculateContentBEnd(mozilla::WritingMode aWritingMode,
|
||||
nsIFrame* aFrame);
|
||||
|
||||
/**
|
||||
* Gets the closest frame (the frame passed in or one of its parents) that
|
||||
|
|
|
@ -585,8 +585,9 @@ nsFieldSetFrame::AccessibleType()
|
|||
#endif
|
||||
|
||||
nscoord
|
||||
nsFieldSetFrame::GetBaseline() const
|
||||
nsFieldSetFrame::GetLogicalBaseline(WritingMode aWritingMode) const
|
||||
{
|
||||
nsIFrame* inner = GetInner();
|
||||
return inner->GetPosition().y + inner->GetBaseline();
|
||||
return inner->BStart(aWritingMode, GetParent()->GetSize().width) +
|
||||
inner->GetLogicalBaseline(aWritingMode);
|
||||
}
|
||||
|
|
|
@ -25,7 +25,7 @@ public:
|
|||
nsSize aCBSize, nscoord aAvailableWidth,
|
||||
nsSize aMargin, nsSize aBorder, nsSize aPadding,
|
||||
uint32_t aFlags) MOZ_OVERRIDE;
|
||||
virtual nscoord GetBaseline() const MOZ_OVERRIDE;
|
||||
virtual nscoord GetLogicalBaseline(mozilla::WritingMode aWritingMode) const MOZ_OVERRIDE;
|
||||
|
||||
/**
|
||||
* The area to paint box-shadows around. It's the border rect except
|
||||
|
|
|
@ -63,14 +63,15 @@ nsFormControlFrame::GetIntrinsicHeight()
|
|||
}
|
||||
|
||||
nscoord
|
||||
nsFormControlFrame::GetBaseline() const
|
||||
nsFormControlFrame::GetLogicalBaseline(WritingMode aWritingMode) const
|
||||
{
|
||||
NS_ASSERTION(!NS_SUBTREE_DIRTY(this),
|
||||
"frame must not be dirty");
|
||||
// Treat radio buttons and checkboxes as having an intrinsic baseline
|
||||
// at the bottom of the control (use the bottom content edge rather
|
||||
// than the bottom margin edge).
|
||||
return mRect.height - GetUsedBorderAndPadding().bottom;
|
||||
return BSize(aWritingMode) -
|
||||
GetLogicalUsedBorderAndPadding(aWritingMode).BEnd(aWritingMode);
|
||||
}
|
||||
|
||||
void
|
||||
|
|
|
@ -45,7 +45,8 @@ public:
|
|||
mozilla::WidgetGUIEvent* aEvent,
|
||||
nsEventStatus* aEventStatus) MOZ_OVERRIDE;
|
||||
|
||||
virtual nscoord GetBaseline() const MOZ_OVERRIDE;
|
||||
virtual nscoord GetLogicalBaseline(mozilla::WritingMode aWritingMode)
|
||||
const MOZ_OVERRIDE;
|
||||
|
||||
/**
|
||||
* Respond to the request to resize and/or reflow
|
||||
|
|
|
@ -333,7 +333,8 @@ nsHTMLButtonControlFrame::ReflowButtonContents(nsPresContext* aPresContext,
|
|||
// Make sure we have a useful 'ascent' value for the child
|
||||
if (contentsDesiredSize.BlockStartAscent() ==
|
||||
nsHTMLReflowMetrics::ASK_FOR_BASELINE) {
|
||||
contentsDesiredSize.SetBlockStartAscent(aFirstKid->GetBaseline());
|
||||
WritingMode wm = aButtonReflowState.GetWritingMode();
|
||||
contentsDesiredSize.SetBlockStartAscent(aFirstKid->GetLogicalBaseline(wm));
|
||||
}
|
||||
|
||||
// OK, we're done with the child frame.
|
||||
|
|
|
@ -714,6 +714,7 @@ TextOverflow::CreateMarkers(const nsLineBox* aLine,
|
|||
if (aCreateLeft) {
|
||||
DisplayListClipState::AutoSaveRestore clipState(mBuilder);
|
||||
|
||||
//XXX Needs vertical text love
|
||||
nsRect markerRect = nsRect(aInsideMarkersArea.x - mLeft.mIntrinsicWidth,
|
||||
aLine->BStart(),
|
||||
mLeft.mIntrinsicWidth, aLine->BSize());
|
||||
|
@ -722,7 +723,7 @@ TextOverflow::CreateMarkers(const nsLineBox* aLine,
|
|||
markerRect, clipState);
|
||||
nsDisplayItem* marker = new (mBuilder)
|
||||
nsDisplayTextOverflowMarker(mBuilder, mBlock, markerRect,
|
||||
aLine->GetAscent(), mLeft.mStyle, 0);
|
||||
aLine->GetLogicalAscent(), mLeft.mStyle, 0);
|
||||
mMarkerList.AppendNewToTop(marker);
|
||||
}
|
||||
|
||||
|
@ -737,7 +738,7 @@ TextOverflow::CreateMarkers(const nsLineBox* aLine,
|
|||
markerRect, clipState);
|
||||
nsDisplayItem* marker = new (mBuilder)
|
||||
nsDisplayTextOverflowMarker(mBuilder, mBlock, markerRect,
|
||||
aLine->GetAscent(), mRight.mStyle, 1);
|
||||
aLine->GetLogicalAscent(), mRight.mStyle, 1);
|
||||
mMarkerList.AppendNewToTop(marker);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1257,6 +1257,19 @@ public:
|
|||
}
|
||||
}
|
||||
|
||||
nsPoint GetPhysicalPosition(WritingMode aWritingMode,
|
||||
nscoord aContainerWidth) const
|
||||
{
|
||||
CHECK_WRITING_MODE(aWritingMode);
|
||||
if (aWritingMode.IsVertical()) {
|
||||
return nsPoint(aWritingMode.IsVerticalLR() ? BStart() : aContainerWidth - BEnd(),
|
||||
IStart());
|
||||
} else {
|
||||
return nsPoint(aWritingMode.IsBidiLTR() ? IStart() : aContainerWidth - IEnd(),
|
||||
BStart());
|
||||
}
|
||||
}
|
||||
|
||||
#if 0 // XXX this would require aContainerWidth as well
|
||||
/**
|
||||
* Return a LogicalRect representing this rect in a different writing mode
|
||||
|
|
|
@ -46,7 +46,7 @@ public:
|
|||
virtual nscoord GetMinWidth(nsRenderingContext *aRenderingContext) MOZ_OVERRIDE;
|
||||
virtual nscoord GetPrefWidth(nsRenderingContext *aRenderingContext) MOZ_OVERRIDE;
|
||||
virtual nsIAtom* GetType() const MOZ_OVERRIDE;
|
||||
virtual nscoord GetBaseline() const MOZ_OVERRIDE;
|
||||
virtual nscoord GetLogicalBaseline(mozilla::WritingMode aWritingMode) const MOZ_OVERRIDE;
|
||||
|
||||
virtual bool IsFrameOfType(uint32_t aFlags) const MOZ_OVERRIDE
|
||||
{
|
||||
|
@ -193,7 +193,7 @@ BRFrame::GetType() const
|
|||
}
|
||||
|
||||
nscoord
|
||||
BRFrame::GetBaseline() const
|
||||
BRFrame::GetLogicalBaseline(mozilla::WritingMode aWritingMode) const
|
||||
{
|
||||
return mAscent;
|
||||
}
|
||||
|
|
|
@ -16,7 +16,7 @@
|
|||
#undef NOISY_FINAL_SIZE // enables debug output for desired width/height computation, once all children have been reflowed
|
||||
#undef NOISY_REMOVE_FRAME
|
||||
#undef NOISY_COMBINED_AREA // enables debug output for combined area computation
|
||||
#undef NOISY_VERTICAL_MARGINS
|
||||
#undef NOISY_BLOCK_DIR_MARGINS
|
||||
#undef NOISY_REFLOW_REASON // gives a little info about why each reflow was requested
|
||||
#undef REFLOW_STATUS_COVERAGE // I think this is most useful for printing, to see which frames return "incomplete"
|
||||
#undef NOISY_FLOATMANAGER // enables debug output for float manager use, useful for analysing reflow of floats
|
||||
|
|
|
@ -488,12 +488,12 @@ nsBlockFrame::InvalidateFrameWithRect(const nsRect& aRect, uint32_t aDisplayItem
|
|||
}
|
||||
|
||||
nscoord
|
||||
nsBlockFrame::GetBaseline() const
|
||||
nsBlockFrame::GetLogicalBaseline(WritingMode aWritingMode) const
|
||||
{
|
||||
nscoord result;
|
||||
if (nsLayoutUtils::GetLastLineBaseline(this, &result))
|
||||
if (nsLayoutUtils::GetLastLineBaseline(aWritingMode, this, &result))
|
||||
return result;
|
||||
return nsFrame::GetBaseline();
|
||||
return nsFrame::GetLogicalBaseline(aWritingMode);
|
||||
}
|
||||
|
||||
nscoord
|
||||
|
@ -1187,12 +1187,14 @@ nsBlockFrame::Reflow(nsPresContext* aPresContext,
|
|||
// XXX Use the entire line when we fix bug 25888.
|
||||
nsLayoutUtils::LinePosition position;
|
||||
WritingMode wm = aReflowState.GetWritingMode();
|
||||
bool havePosition = nsLayoutUtils::GetFirstLinePosition(this, &position);
|
||||
nscoord lineTop = havePosition ? position.mTop
|
||||
: reflowState->ComputedPhysicalBorderPadding().top;
|
||||
bool havePosition = nsLayoutUtils::GetFirstLinePosition(wm, this,
|
||||
&position);
|
||||
nscoord lineTop = havePosition ?
|
||||
position.mBStart :
|
||||
reflowState->ComputedLogicalBorderPadding().BStart(wm);
|
||||
nsIFrame* bullet = GetOutsideBullet();
|
||||
ReflowBullet(bullet, state, metrics, lineTop);
|
||||
NS_ASSERTION(!BulletIsEmpty() || metrics.Height() == 0,
|
||||
NS_ASSERTION(!BulletIsEmpty() || metrics.BSize(wm) == 0,
|
||||
"empty bullet took up space");
|
||||
|
||||
if (havePosition && !BulletIsEmpty()) {
|
||||
|
@ -2461,7 +2463,8 @@ nsBlockFrame::ReflowDirtyLines(nsBlockReflowState& aState)
|
|||
|
||||
if (metrics.BlockStartAscent() == nsHTMLReflowMetrics::ASK_FOR_BASELINE) {
|
||||
nscoord ascent;
|
||||
if (nsLayoutUtils::GetFirstLineBaseline(bullet, &ascent)) {
|
||||
WritingMode wm = aState.mReflowState.GetWritingMode();
|
||||
if (nsLayoutUtils::GetFirstLineBaseline(wm, bullet, &ascent)) {
|
||||
metrics.SetBlockStartAscent(ascent);
|
||||
} else {
|
||||
metrics.SetBlockStartAscent(metrics.BSize(wm));
|
||||
|
@ -3016,8 +3019,10 @@ nsBlockFrame::ReflowBlockFrame(nsBlockReflowState& aState,
|
|||
|
||||
// Now compute the collapsed margin-top value into aState.mPrevBottomMargin, assuming
|
||||
// that all child margins collapse down to clearanceFrame.
|
||||
nsBlockReflowContext::ComputeCollapsedTopMargin(reflowState,
|
||||
&aState.mPrevBottomMargin, clearanceFrame, &mayNeedRetry);
|
||||
nsBlockReflowContext::ComputeCollapsedBStartMargin(reflowState,
|
||||
&aState.mPrevBottomMargin,
|
||||
clearanceFrame,
|
||||
&mayNeedRetry);
|
||||
|
||||
// XXX optimization; we could check the collapsing children to see if they are sure
|
||||
// to require clearance, and so avoid retrying them
|
||||
|
@ -3050,8 +3055,10 @@ nsBlockFrame::ReflowBlockFrame(nsBlockReflowState& aState,
|
|||
|
||||
// Compute the collapsed margin again, ignoring the incoming margin this time
|
||||
mayNeedRetry = false;
|
||||
nsBlockReflowContext::ComputeCollapsedTopMargin(reflowState,
|
||||
&aState.mPrevBottomMargin, clearanceFrame, &mayNeedRetry);
|
||||
nsBlockReflowContext::ComputeCollapsedBStartMargin(reflowState,
|
||||
&aState.mPrevBottomMargin,
|
||||
clearanceFrame,
|
||||
&mayNeedRetry);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -3207,8 +3214,7 @@ nsBlockFrame::ReflowBlockFrame(nsBlockReflowState& aState,
|
|||
*aKeepReflowGoing = brc.PlaceBlock(blockHtmlRS, forceFit, aLine.get(),
|
||||
collapsedBottomMargin,
|
||||
overflowAreas,
|
||||
frameReflowStatus,
|
||||
aState.mContainerWidth);
|
||||
frameReflowStatus);
|
||||
if (!NS_FRAME_IS_FULLY_COMPLETE(frameReflowStatus) &&
|
||||
ShouldAvoidBreakInside(aState.mReflowState)) {
|
||||
*aKeepReflowGoing = false;
|
||||
|
@ -3297,7 +3303,7 @@ nsBlockFrame::ReflowBlockFrame(nsBlockReflowState& aState,
|
|||
// flow block. Since we just continued the child block frame,
|
||||
// we know that line->mFirstChild is not the last flow block
|
||||
// therefore zero out the running margin value.
|
||||
#ifdef NOISY_VERTICAL_MARGINS
|
||||
#ifdef NOISY_BLOCK_DIR_MARGINS
|
||||
ListTag(stdout);
|
||||
printf(": reflow incomplete, frame=");
|
||||
nsFrame::ListTag(stdout, frame);
|
||||
|
@ -3325,7 +3331,7 @@ nsBlockFrame::ReflowBlockFrame(nsBlockReflowState& aState,
|
|||
aState.mOverflowTracker->Insert(nextFrame, frameReflowStatus);
|
||||
NS_MergeReflowStatusInto(&aState.mReflowStatus, frameReflowStatus);
|
||||
|
||||
#ifdef NOISY_VERTICAL_MARGINS
|
||||
#ifdef NOISY_BLOCK_DIR_MARGINS
|
||||
ListTag(stdout);
|
||||
printf(": reflow complete but overflow incomplete for ");
|
||||
nsFrame::ListTag(stdout, frame);
|
||||
|
@ -3336,7 +3342,7 @@ nsBlockFrame::ReflowBlockFrame(nsBlockReflowState& aState,
|
|||
}
|
||||
}
|
||||
else { // frame is fully complete
|
||||
#ifdef NOISY_VERTICAL_MARGINS
|
||||
#ifdef NOISY_BLOCK_DIR_MARGINS
|
||||
ListTag(stdout);
|
||||
printf(": reflow complete for ");
|
||||
nsFrame::ListTag(stdout, frame);
|
||||
|
@ -3345,12 +3351,12 @@ nsBlockFrame::ReflowBlockFrame(nsBlockReflowState& aState,
|
|||
#endif
|
||||
aState.mPrevBottomMargin = collapsedBottomMargin;
|
||||
}
|
||||
#ifdef NOISY_VERTICAL_MARGINS
|
||||
#ifdef NOISY_BLOCK_DIR_MARGINS
|
||||
ListTag(stdout);
|
||||
printf(": frame=");
|
||||
nsFrame::ListTag(stdout, frame);
|
||||
printf(" carriedOutBottomMargin=%d collapsedBottomMargin=%d => %d\n",
|
||||
brc.GetCarriedOutBottomMargin(), collapsedBottomMargin.get(),
|
||||
printf(" carriedOutBEndMargin=%d collapsedBottomMargin=%d => %d\n",
|
||||
brc.GetCarriedOutBEndMargin(), collapsedBottomMargin.get(),
|
||||
aState.mPrevBottomMargin);
|
||||
#endif
|
||||
} else {
|
||||
|
@ -4102,7 +4108,7 @@ nsBlockFrame::PlaceLine(nsBlockReflowState& aState,
|
|||
aLineLayout.AddBulletFrame(bullet, metrics);
|
||||
addedBullet = true;
|
||||
}
|
||||
aLineLayout.BlockDirAlignLine();
|
||||
aLineLayout.VerticalAlignLine();
|
||||
|
||||
// We want to compare to the available space that we would have had in
|
||||
// the line's height *before* we placed any floats in the line itself.
|
||||
|
@ -4164,7 +4170,7 @@ nsBlockFrame::PlaceLine(nsBlockReflowState& aState,
|
|||
(aLineLayout.GetLineEndsInBR() ||
|
||||
IsLastLine(aState, aLine)));
|
||||
|
||||
aLineLayout.InlineDirAlignFrames(aLine, isLastLine);
|
||||
aLineLayout.TextAlignLine(aLine, isLastLine);
|
||||
|
||||
// From here on, pfd->mBounds rectangles are incorrect because bidi
|
||||
// might have moved frames around!
|
||||
|
@ -5785,8 +5791,9 @@ nsBlockFrame::ReflowFloat(nsBlockReflowState& aState,
|
|||
floatRS.mDiscoveredClearance = nullptr;
|
||||
// Only first in flow gets a top margin.
|
||||
if (!aFloat->GetPrevInFlow()) {
|
||||
nsBlockReflowContext::ComputeCollapsedTopMargin(floatRS, &margin,
|
||||
clearanceFrame, &mayNeedRetry);
|
||||
nsBlockReflowContext::ComputeCollapsedBStartMargin(floatRS, &margin,
|
||||
clearanceFrame,
|
||||
&mayNeedRetry);
|
||||
|
||||
if (mayNeedRetry && !clearanceFrame) {
|
||||
floatRS.mDiscoveredClearance = &clearanceFrame;
|
||||
|
|
|
@ -121,7 +121,7 @@ public:
|
|||
nsIFrame* aOldFrame) MOZ_OVERRIDE;
|
||||
virtual const nsFrameList& GetChildList(ChildListID aListID) const MOZ_OVERRIDE;
|
||||
virtual void GetChildLists(nsTArray<ChildList>* aLists) const MOZ_OVERRIDE;
|
||||
virtual nscoord GetBaseline() const MOZ_OVERRIDE;
|
||||
virtual nscoord GetLogicalBaseline(mozilla::WritingMode aWritingMode) const MOZ_OVERRIDE;
|
||||
virtual nscoord GetCaretBaseline() const MOZ_OVERRIDE;
|
||||
virtual void DestroyFrom(nsIFrame* aDestructRoot) MOZ_OVERRIDE;
|
||||
virtual nsSplittableType GetSplittableType() const MOZ_OVERRIDE;
|
||||
|
|
|
@ -14,21 +14,24 @@
|
|||
#include "nsLineBox.h"
|
||||
#include "nsLayoutUtils.h"
|
||||
|
||||
using namespace mozilla;
|
||||
|
||||
#ifdef DEBUG
|
||||
#undef NOISY_MAX_ELEMENT_SIZE
|
||||
#undef REALLY_NOISY_MAX_ELEMENT_SIZE
|
||||
#undef NOISY_VERTICAL_MARGINS
|
||||
#undef NOISY_BLOCK_DIR_MARGINS
|
||||
#else
|
||||
#undef NOISY_MAX_ELEMENT_SIZE
|
||||
#undef REALLY_NOISY_MAX_ELEMENT_SIZE
|
||||
#undef NOISY_VERTICAL_MARGINS
|
||||
#undef NOISY_BLOCK_DIR_MARGINS
|
||||
#endif
|
||||
|
||||
nsBlockReflowContext::nsBlockReflowContext(nsPresContext* aPresContext,
|
||||
const nsHTMLReflowState& aParentRS)
|
||||
: mPresContext(aPresContext),
|
||||
mOuterReflowState(aParentRS),
|
||||
mMetrics(aParentRS.GetWritingMode())
|
||||
mSpace(aParentRS.GetWritingMode()),
|
||||
mMetrics(aParentRS)
|
||||
{
|
||||
}
|
||||
|
||||
|
@ -41,39 +44,42 @@ static nsIFrame* DescendIntoBlockLevelFrame(nsIFrame* aFrame)
|
|||
}
|
||||
|
||||
bool
|
||||
nsBlockReflowContext::ComputeCollapsedTopMargin(const nsHTMLReflowState& aRS,
|
||||
nsCollapsingMargin* aMargin, nsIFrame* aClearanceFrame,
|
||||
bool* aMayNeedRetry, bool* aBlockIsEmpty)
|
||||
nsBlockReflowContext::ComputeCollapsedBStartMargin(const nsHTMLReflowState& aRS,
|
||||
nsCollapsingMargin* aMargin,
|
||||
nsIFrame* aClearanceFrame,
|
||||
bool* aMayNeedRetry,
|
||||
bool* aBlockIsEmpty)
|
||||
{
|
||||
// Include frame's top margin
|
||||
aMargin->Include(aRS.ComputedPhysicalMargin().top);
|
||||
WritingMode wm = aRS.GetWritingMode();
|
||||
// Include frame's block-start margin
|
||||
aMargin->Include(aRS.ComputedLogicalMargin().BStart(wm));
|
||||
|
||||
// The inclusion of the bottom margin when empty is done by the caller
|
||||
// The inclusion of the block-end margin when empty is done by the caller
|
||||
// since it doesn't need to be done by the top-level (non-recursive)
|
||||
// caller.
|
||||
|
||||
#ifdef NOISY_VERTICAL_MARGINS
|
||||
#ifdef NOISY_BLOCKDIR_MARGINS
|
||||
nsFrame::ListTag(stdout, aRS.frame);
|
||||
printf(": %d => %d\n", aRS.ComputedPhysicalMargin().top, aMargin->get());
|
||||
printf(": %d => %d\n", aRS.ComputedLogicalMargin().BStart(wm), aMargin->get());
|
||||
#endif
|
||||
|
||||
bool dirtiedLine = false;
|
||||
bool setBlockIsEmpty = false;
|
||||
|
||||
// Calculate the frame's generational top-margin from its child
|
||||
// blocks. Note that if the frame has a non-zero top-border or
|
||||
// top-padding then this step is skipped because it will be a margin
|
||||
// Calculate the frame's generational block-start-margin from its child
|
||||
// blocks. Note that if the frame has a non-zero block-start-border or
|
||||
// block-start-padding then this step is skipped because it will be a margin
|
||||
// root. It is also skipped if the frame is a margin root for other
|
||||
// reasons.
|
||||
nsIFrame* frame = DescendIntoBlockLevelFrame(aRS.frame);
|
||||
nsPresContext* prescontext = frame->PresContext();
|
||||
nsBlockFrame* block = nullptr;
|
||||
if (0 == aRS.ComputedPhysicalBorderPadding().top) {
|
||||
if (0 == aRS.ComputedLogicalBorderPadding().BStart(wm)) {
|
||||
block = nsLayoutUtils::GetAsBlock(frame);
|
||||
if (block) {
|
||||
bool topMarginRoot, unused;
|
||||
block->IsMarginRoot(&topMarginRoot, &unused);
|
||||
if (topMarginRoot) {
|
||||
bool bStartMarginRoot, unused;
|
||||
block->IsMarginRoot(&bStartMarginRoot, &unused);
|
||||
if (bStartMarginRoot) {
|
||||
block = nullptr;
|
||||
}
|
||||
}
|
||||
|
@ -112,7 +118,7 @@ nsBlockReflowContext::ComputeCollapsedTopMargin(const nsHTMLReflowState& aRS,
|
|||
line->MarkDirty();
|
||||
dirtiedLine = true;
|
||||
}
|
||||
|
||||
|
||||
bool isEmpty;
|
||||
if (line->IsInline()) {
|
||||
isEmpty = line->IsEmpty();
|
||||
|
@ -129,7 +135,7 @@ nsBlockReflowContext::ComputeCollapsedTopMargin(const nsHTMLReflowState& aRS,
|
|||
// child blocks margin and so in so that we can look into
|
||||
// it. For its margins to be computed we need to have a reflow
|
||||
// state for it.
|
||||
|
||||
|
||||
// We may have to construct an extra reflow state here if
|
||||
// we drilled down through a block wrapper. At the moment
|
||||
// we can only drill down one level so we only have to support
|
||||
|
@ -153,12 +159,18 @@ nsBlockReflowContext::ComputeCollapsedTopMargin(const nsHTMLReflowState& aRS,
|
|||
if (kid->StyleDisplay()->mBreakType != NS_STYLE_CLEAR_NONE) {
|
||||
*aMayNeedRetry = true;
|
||||
}
|
||||
if (ComputeCollapsedTopMargin(innerReflowState, aMargin, aClearanceFrame, aMayNeedRetry, &isEmpty)) {
|
||||
if (ComputeCollapsedBStartMargin(innerReflowState, aMargin,
|
||||
aClearanceFrame, aMayNeedRetry,
|
||||
&isEmpty)) {
|
||||
line->MarkDirty();
|
||||
dirtiedLine = true;
|
||||
}
|
||||
if (isEmpty)
|
||||
aMargin->Include(innerReflowState.ComputedPhysicalMargin().bottom);
|
||||
if (isEmpty) {
|
||||
WritingMode innerWM = innerReflowState.GetWritingMode();
|
||||
LogicalMargin innerMargin =
|
||||
innerReflowState.ComputedLogicalMargin().ConvertTo(wm, innerWM);
|
||||
aMargin->Include(innerMargin.BEnd(wm));
|
||||
}
|
||||
}
|
||||
if (outerReflowState != &aRS) {
|
||||
delete const_cast<nsHTMLReflowState*>(outerReflowState);
|
||||
|
@ -187,7 +199,7 @@ nsBlockReflowContext::ComputeCollapsedTopMargin(const nsHTMLReflowState& aRS,
|
|||
*aBlockIsEmpty = aRS.frame->IsEmpty();
|
||||
}
|
||||
|
||||
#ifdef NOISY_VERTICAL_MARGINS
|
||||
#ifdef NOISY_BLOCKDIR_MARGINS
|
||||
nsFrame::ListTag(stdout, aRS.frame);
|
||||
printf(": => %d\n", aMargin->get());
|
||||
#endif
|
||||
|
@ -197,55 +209,67 @@ nsBlockReflowContext::ComputeCollapsedTopMargin(const nsHTMLReflowState& aRS,
|
|||
|
||||
void
|
||||
nsBlockReflowContext::ReflowBlock(const nsRect& aSpace,
|
||||
bool aApplyTopMargin,
|
||||
bool aApplyBStartMargin,
|
||||
nsCollapsingMargin& aPrevMargin,
|
||||
nscoord aClearance,
|
||||
bool aIsAdjacentWithTop,
|
||||
bool aIsAdjacentWithBStart,
|
||||
nsLineBox* aLine,
|
||||
nsHTMLReflowState& aFrameRS,
|
||||
nsReflowStatus& aFrameReflowStatus,
|
||||
nsBlockReflowState& aState)
|
||||
{
|
||||
mFrame = aFrameRS.frame;
|
||||
mSpace = aSpace;
|
||||
mWritingMode = aState.mReflowState.GetWritingMode();
|
||||
mContainerWidth = aState.mContainerWidth;
|
||||
mSpace = LogicalRect(mWritingMode, aSpace, mContainerWidth);
|
||||
|
||||
if (!aIsAdjacentWithTop) {
|
||||
if (!aIsAdjacentWithBStart) {
|
||||
aFrameRS.mFlags.mIsTopOfPage = false; // make sure this is cleared
|
||||
}
|
||||
|
||||
if (aApplyTopMargin) {
|
||||
mTopMargin = aPrevMargin;
|
||||
if (aApplyBStartMargin) {
|
||||
mBStartMargin = aPrevMargin;
|
||||
|
||||
#ifdef NOISY_VERTICAL_MARGINS
|
||||
#ifdef NOISY_BLOCKDIR_MARGINS
|
||||
nsFrame::ListTag(stdout, mOuterReflowState.frame);
|
||||
printf(": reflowing ");
|
||||
nsFrame::ListTag(stdout, mFrame);
|
||||
printf(" margin => %d, clearance => %d\n", mTopMargin.get(), aClearance);
|
||||
printf(" margin => %d, clearance => %d\n", mBStartMargin.get(), aClearance);
|
||||
#endif
|
||||
|
||||
// Adjust the available height if its constrained so that the
|
||||
// Adjust the available block size if it's constrained so that the
|
||||
// child frame doesn't think it can reflow into its margin area.
|
||||
if (NS_UNCONSTRAINEDSIZE != aFrameRS.AvailableHeight()) {
|
||||
aFrameRS.AvailableHeight() -= mTopMargin.get() + aClearance;
|
||||
if (NS_UNCONSTRAINEDSIZE != aFrameRS.AvailableBSize()) {
|
||||
aFrameRS.AvailableBSize() -= mBStartMargin.get() + aClearance;
|
||||
}
|
||||
}
|
||||
|
||||
nscoord tx = 0, ty = 0;
|
||||
// The values of x and y do not matter for floats, so don't bother calculating
|
||||
// them. Floats are guaranteed to have their own float manager, so tx and ty
|
||||
// don't matter. mX and mY don't matter becacuse they are only used in
|
||||
// PlaceBlock, which is not used for floats.
|
||||
nscoord tI = 0, tB = 0;
|
||||
// The values of x and y do not matter for floats, so don't bother
|
||||
// calculating them. Floats are guaranteed to have their own float
|
||||
// manager, so tI and tB don't matter. mICoord and mBCoord don't
|
||||
// matter becacuse they are only used in PlaceBlock, which is not used
|
||||
// for floats.
|
||||
if (aLine) {
|
||||
// Compute x/y coordinate where reflow will begin. Use the rules
|
||||
// from 10.3.3 to determine what to apply. At this point in the
|
||||
// reflow auto left/right margins will have a zero value.
|
||||
// Compute inline/block coordinate where reflow will begin. Use the
|
||||
// rules from 10.3.3 to determine what to apply. At this point in the
|
||||
// reflow auto inline-start/end margins will have a zero value.
|
||||
|
||||
mX = tx = mSpace.x + aFrameRS.ComputedPhysicalMargin().left;
|
||||
mY = ty = mSpace.y + mTopMargin.get() + aClearance;
|
||||
WritingMode frameWM = aFrameRS.GetWritingMode();
|
||||
mICoord = tI =
|
||||
mSpace.IStart(mWritingMode) +
|
||||
aFrameRS.ComputedLogicalMargin().ConvertTo(mWritingMode,
|
||||
frameWM).IStart(mWritingMode);
|
||||
mBCoord = tB = mSpace.BStart(mWritingMode) +
|
||||
mBStartMargin.get() + aClearance;
|
||||
|
||||
//XXX temporary until nsFloatManager is logicalized
|
||||
tI = aSpace.x + aFrameRS.ComputedPhysicalMargin().left;
|
||||
tB = aSpace.y + mBStartMargin.get() + aClearance;
|
||||
|
||||
if ((mFrame->GetStateBits() & NS_BLOCK_FLOAT_MGR) == 0)
|
||||
aFrameRS.mBlockDelta =
|
||||
mOuterReflowState.mBlockDelta + ty - aLine->BStart();
|
||||
mOuterReflowState.mBlockDelta + mBCoord - aLine->BStart();
|
||||
}
|
||||
|
||||
// Let frame know that we are reflowing it
|
||||
|
@ -256,9 +280,9 @@ nsBlockReflowContext::ReflowBlock(const nsRect& aSpace,
|
|||
mMetrics.Height() = nscoord(0xdeadbeef);
|
||||
#endif
|
||||
|
||||
mOuterReflowState.mFloatManager->Translate(tx, ty);
|
||||
mOuterReflowState.mFloatManager->Translate(tI, tB);
|
||||
mFrame->Reflow(mPresContext, mMetrics, aFrameRS, aFrameReflowStatus);
|
||||
mOuterReflowState.mFloatManager->Translate(-tx, -ty);
|
||||
mOuterReflowState.mFloatManager->Translate(-tI, -tB);
|
||||
|
||||
#ifdef DEBUG
|
||||
if (!NS_INLINE_IS_BREAK_BEFORE(aFrameReflowStatus)) {
|
||||
|
@ -303,92 +327,100 @@ nsBlockReflowContext::ReflowBlock(const nsRect& aSpace,
|
|||
|
||||
/**
|
||||
* Attempt to place the block frame within the available space. If
|
||||
* it fits, apply horizontal positioning (CSS 10.3.3), collapse
|
||||
* margins (CSS2 8.3.1). Also apply relative positioning.
|
||||
* it fits, apply inline-dir ("horizontal") positioning (CSS 10.3.3),
|
||||
* collapse margins (CSS2 8.3.1). Also apply relative positioning.
|
||||
*/
|
||||
bool
|
||||
nsBlockReflowContext::PlaceBlock(const nsHTMLReflowState& aReflowState,
|
||||
bool aForceFit,
|
||||
nsLineBox* aLine,
|
||||
nsCollapsingMargin& aBottomMarginResult,
|
||||
nsCollapsingMargin& aBEndMarginResult,
|
||||
nsOverflowAreas& aOverflowAreas,
|
||||
nsReflowStatus aReflowStatus,
|
||||
nscoord aContainerWidth)
|
||||
nsReflowStatus aReflowStatus)
|
||||
{
|
||||
// Compute collapsed bottom margin value.
|
||||
// Compute collapsed block-end margin value.
|
||||
WritingMode wm = aReflowState.GetWritingMode();
|
||||
WritingMode parentWM = mMetrics.GetWritingMode();
|
||||
if (NS_FRAME_IS_COMPLETE(aReflowStatus)) {
|
||||
aBottomMarginResult = mMetrics.mCarriedOutBottomMargin;
|
||||
aBottomMarginResult.Include(aReflowState.ComputedPhysicalMargin().bottom);
|
||||
aBEndMarginResult = mMetrics.mCarriedOutBottomMargin;
|
||||
aBEndMarginResult.Include(aReflowState.ComputedLogicalMargin().BEnd(wm));
|
||||
} else {
|
||||
// The used bottom-margin is set to zero above a break.
|
||||
aBottomMarginResult.Zero();
|
||||
aBEndMarginResult.Zero();
|
||||
}
|
||||
|
||||
nsPoint position(mX, mY);
|
||||
nscoord backupContainingBlockAdvance = 0;
|
||||
|
||||
// Check whether the block's bottom margin collapses with its top
|
||||
// Check whether the block's block-end margin collapses with its block-start
|
||||
// margin. See CSS 2.1 section 8.3.1; those rules seem to match
|
||||
// nsBlockFrame::IsEmpty(). Any such block must have zero height so
|
||||
// nsBlockFrame::IsEmpty(). Any such block must have zero block-size so
|
||||
// check that first. Note that a block can have clearance and still
|
||||
// have adjoining top/bottom margins, because the clearance goes
|
||||
// above the top margin.
|
||||
// have adjoining block-start/end margins, because the clearance goes
|
||||
// above the block-start margin.
|
||||
// Mark the frame as non-dirty; it has been reflowed (or we wouldn't
|
||||
// be here), and we don't want to assert in CachedIsEmpty()
|
||||
mFrame->RemoveStateBits(NS_FRAME_IS_DIRTY);
|
||||
bool empty = 0 == mMetrics.Height() && aLine->CachedIsEmpty();
|
||||
bool empty = 0 == mMetrics.BSize(parentWM) && aLine->CachedIsEmpty();
|
||||
if (empty) {
|
||||
// Collapse the bottom margin with the top margin that was already
|
||||
// applied.
|
||||
aBottomMarginResult.Include(mTopMargin);
|
||||
// Collapse the block-end margin with the block-start margin that was
|
||||
// already applied.
|
||||
aBEndMarginResult.Include(mBStartMargin);
|
||||
|
||||
#ifdef NOISY_VERTICAL_MARGINS
|
||||
#ifdef NOISY_BLOCKDIR_MARGINS
|
||||
printf(" ");
|
||||
nsFrame::ListTag(stdout, mOuterReflowState.frame);
|
||||
printf(": ");
|
||||
nsFrame::ListTag(stdout, mFrame);
|
||||
printf(" -- collapsing top & bottom margin together; y=%d spaceY=%d\n",
|
||||
position.y, mSpace.y);
|
||||
printf(" -- collapsing block start & end margin together; BStart=%d spaceBStart=%d\n",
|
||||
mBCoord, mSpace.BStart(mWritingMode));
|
||||
#endif
|
||||
// Section 8.3.1 of CSS 2.1 says that blocks with adjoining
|
||||
// top/bottom margins whose top margin collapses with their
|
||||
// parent's top margin should have their top border-edge at the
|
||||
// "top/bottom" (i.e. block-start/end) margins whose top margin collapses
|
||||
// with their parent's top margin should have their top border-edge at the
|
||||
// top border-edge of their parent. We actually don't have to do
|
||||
// anything special to make this happen. In that situation,
|
||||
// nsBlockFrame::ShouldApplyTopMargin will have returned false,
|
||||
// and mTopMargin and aClearance will have been zero in
|
||||
// nsBlockFrame::ShouldApplyBStartMargin will have returned false,
|
||||
// and mBStartMargin and aClearance will have been zero in
|
||||
// ReflowBlock.
|
||||
|
||||
// If we did apply our top margin, but now we're collapsing it
|
||||
// into the bottom margin, we need to back up the containing
|
||||
// block's y-advance by our top margin so that it doesn't get
|
||||
// If we did apply our block-start margin, but now we're collapsing it
|
||||
// into the block-end margin, we need to back up the containing
|
||||
// block's bCoord-advance by our block-start margin so that it doesn't get
|
||||
// counted twice. Note that here we're allowing the line's bounds
|
||||
// to become different from the block's position; we do this
|
||||
// because the containing block will place the next line at the
|
||||
// line's YMost, and it must place the next line at a different
|
||||
// line's BEnd, and it must place the next line at a different
|
||||
// point from where this empty block will be.
|
||||
backupContainingBlockAdvance = mTopMargin.get();
|
||||
backupContainingBlockAdvance = mBStartMargin.get();
|
||||
}
|
||||
|
||||
// See if the frame fit. If it's the first frame or empty then it
|
||||
// always fits. If the height is unconstrained then it always fits,
|
||||
// even if there's some sort of integer overflow that makes y +
|
||||
// mMetrics.Height() appear to go beyond the available height.
|
||||
if (!empty && !aForceFit && mSpace.height != NS_UNCONSTRAINEDSIZE) {
|
||||
nscoord yMost = position.y - backupContainingBlockAdvance + mMetrics.Height();
|
||||
if (yMost > mSpace.YMost()) {
|
||||
// always fits. If the block-size is unconstrained then it always fits,
|
||||
// even if there's some sort of integer overflow that makes bCoord +
|
||||
// mMetrics.BSize() appear to go beyond the available height.
|
||||
if (!empty && !aForceFit &&
|
||||
mSpace.BSize(mWritingMode) != NS_UNCONSTRAINEDSIZE) {
|
||||
nscoord bEnd = mBCoord -
|
||||
backupContainingBlockAdvance + mMetrics.BSize(mWritingMode);
|
||||
if (bEnd > mSpace.BEnd(mWritingMode)) {
|
||||
// didn't fit, we must acquit.
|
||||
mFrame->DidReflow(mPresContext, &aReflowState, nsDidReflowStatus::FINISHED);
|
||||
mFrame->DidReflow(mPresContext, &aReflowState,
|
||||
nsDidReflowStatus::FINISHED);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
aLine->SetBounds(aReflowState.GetWritingMode(),
|
||||
nsRect(position.x,
|
||||
position.y - backupContainingBlockAdvance,
|
||||
mMetrics.Width(),
|
||||
mMetrics.Height()),
|
||||
aContainerWidth);
|
||||
aLine->SetBounds(mWritingMode,
|
||||
mICoord, mBCoord - backupContainingBlockAdvance,
|
||||
mMetrics.ISize(mWritingMode), mMetrics.BSize(mWritingMode),
|
||||
mContainerWidth);
|
||||
|
||||
// XXX temporary until other classes are logicalized
|
||||
nsPoint position = LogicalRect(mWritingMode,
|
||||
mICoord, mBCoord,
|
||||
mMetrics.ISize(mWritingMode),
|
||||
mMetrics.BSize(mWritingMode)).
|
||||
GetPhysicalPosition(mWritingMode, mContainerWidth);
|
||||
|
||||
aReflowState.ApplyRelativePositioning(&position);
|
||||
|
||||
|
|
|
@ -29,10 +29,10 @@ public:
|
|||
~nsBlockReflowContext() { }
|
||||
|
||||
void ReflowBlock(const nsRect& aSpace,
|
||||
bool aApplyTopMargin,
|
||||
bool aApplyBStartMargin,
|
||||
nsCollapsingMargin& aPrevMargin,
|
||||
nscoord aClearance,
|
||||
bool aIsAdjacentWithTop,
|
||||
bool aIsAdjacentWithBStart,
|
||||
nsLineBox* aLine,
|
||||
nsHTMLReflowState& aReflowState,
|
||||
nsReflowStatus& aReflowStatus,
|
||||
|
@ -41,51 +41,52 @@ public:
|
|||
bool PlaceBlock(const nsHTMLReflowState& aReflowState,
|
||||
bool aForceFit,
|
||||
nsLineBox* aLine,
|
||||
nsCollapsingMargin& aBottomMarginResult /* out */,
|
||||
nsCollapsingMargin& aBEndMarginResult /* out */,
|
||||
nsOverflowAreas& aOverflowAreas,
|
||||
nsReflowStatus aReflowStatus,
|
||||
nscoord aContainerWidth);
|
||||
nsReflowStatus aReflowStatus);
|
||||
|
||||
nsCollapsingMargin& GetCarriedOutBottomMargin() {
|
||||
nsCollapsingMargin& GetCarriedOutBEndMargin() {
|
||||
return mMetrics.mCarriedOutBottomMargin;
|
||||
}
|
||||
|
||||
nscoord GetTopMargin() const {
|
||||
return mTopMargin.get();
|
||||
}
|
||||
|
||||
const nsHTMLReflowMetrics& GetMetrics() const {
|
||||
return mMetrics;
|
||||
}
|
||||
|
||||
/**
|
||||
* Computes the collapsed top margin for a block whose reflow state is in aRS.
|
||||
* Computes the collapsed block-start margin for a block whose reflow state
|
||||
* is in aRS.
|
||||
* The computed margin is added into aMargin.
|
||||
* If aClearanceFrame is null then this is the first optimistic pass which shall assume
|
||||
* that no frames have clearance, and we clear the HasClearance on all frames encountered.
|
||||
* If non-null, this is the second pass and
|
||||
* the caller has decided aClearanceFrame needs clearance (and we will
|
||||
* therefore stop collapsing there); also, this function is responsible for marking
|
||||
* it with SetHasClearance.
|
||||
* If in the optimistic pass any frame is encountered that might possibly need
|
||||
* clearance (i.e., if we really needed the optimism assumption) then we set aMayNeedRetry
|
||||
* to true.
|
||||
* We return true if we changed the clearance state of any line and marked it dirty.
|
||||
* If aClearanceFrame is null then this is the first optimistic pass which
|
||||
* shall assume that no frames have clearance, and we clear the HasClearance
|
||||
* on all frames encountered.
|
||||
* If non-null, this is the second pass and the caller has decided
|
||||
* aClearanceFrame needs clearance (and we will therefore stop collapsing
|
||||
* there); also, this function is responsible for marking it with
|
||||
* SetHasClearance.
|
||||
* If in the optimistic pass any frame is encountered that might possibly
|
||||
* need clearance (i.e., if we really needed the optimism assumption) then
|
||||
* we set aMayNeedRetry to true.
|
||||
* We return true if we changed the clearance state of any line and marked it
|
||||
* dirty.
|
||||
*/
|
||||
static bool ComputeCollapsedTopMargin(const nsHTMLReflowState& aRS,
|
||||
nsCollapsingMargin* aMargin, nsIFrame* aClearanceFrame,
|
||||
bool* aMayNeedRetry, bool* aIsEmpty = nullptr);
|
||||
static bool ComputeCollapsedBStartMargin(const nsHTMLReflowState& aRS,
|
||||
nsCollapsingMargin* aMargin,
|
||||
nsIFrame* aClearanceFrame,
|
||||
bool* aMayNeedRetry,
|
||||
bool* aIsEmpty = nullptr);
|
||||
|
||||
protected:
|
||||
nsPresContext* mPresContext;
|
||||
const nsHTMLReflowState& mOuterReflowState;
|
||||
|
||||
nsIFrame* mFrame;
|
||||
nsRect mSpace;
|
||||
mozilla::LogicalRect mSpace;
|
||||
|
||||
nscoord mX, mY;
|
||||
nscoord mICoord, mBCoord, mContainerWidth;
|
||||
mozilla::WritingMode mWritingMode;
|
||||
nsHTMLReflowMetrics mMetrics;
|
||||
nsCollapsingMargin mTopMargin;
|
||||
nsCollapsingMargin mBStartMargin;
|
||||
};
|
||||
|
||||
#endif /* nsBlockReflowContext_h___ */
|
||||
|
|
|
@ -781,11 +781,11 @@ nsBulletFrame::GetImage() const
|
|||
}
|
||||
|
||||
nscoord
|
||||
nsBulletFrame::GetBaseline() const
|
||||
nsBulletFrame::GetLogicalBaseline(WritingMode aWritingMode) const
|
||||
{
|
||||
nscoord ascent = 0, bottomPadding;
|
||||
if (GetStateBits() & BULLET_FRAME_IMAGE_LOADING) {
|
||||
ascent = GetRect().height;
|
||||
ascent = BSize(aWritingMode);
|
||||
} else {
|
||||
nsRefPtr<nsFontMetrics> fm;
|
||||
nsLayoutUtils::GetFontMetricsForFrame(this, getter_AddRefs(fm),
|
||||
|
@ -820,7 +820,8 @@ nsBulletFrame::GetBaseline() const
|
|||
break;
|
||||
}
|
||||
}
|
||||
return ascent + GetUsedBorderAndPadding().top;
|
||||
return ascent +
|
||||
GetLogicalUsedMargin(aWritingMode).BStart(aWritingMode);
|
||||
}
|
||||
|
||||
void
|
||||
|
|
|
@ -86,7 +86,7 @@ public:
|
|||
|
||||
virtual bool IsEmpty() MOZ_OVERRIDE;
|
||||
virtual bool IsSelfEmpty() MOZ_OVERRIDE;
|
||||
virtual nscoord GetBaseline() const MOZ_OVERRIDE;
|
||||
virtual nscoord GetLogicalBaseline(mozilla::WritingMode aWritingMode) const MOZ_OVERRIDE;
|
||||
|
||||
float GetFontSizeInflation() const;
|
||||
bool HasFontSizeInflation() const {
|
||||
|
|
|
@ -464,7 +464,7 @@ nsColumnSetFrame::ReflowChildren(nsHTMLReflowMetrics& aDesiredSize,
|
|||
}
|
||||
}
|
||||
int columnCount = 0;
|
||||
int contentBottom = 0;
|
||||
int contentBEnd = 0;
|
||||
bool reflowNext = false;
|
||||
|
||||
while (child) {
|
||||
|
@ -494,7 +494,8 @@ nsColumnSetFrame::ReflowChildren(nsHTMLReflowMetrics& aDesiredSize,
|
|||
bool skipResizeHeightShrink = shrinkingHeightOnly
|
||||
&& child->GetScrollableOverflowRect().YMost() <= aConfig.mColMaxHeight;
|
||||
|
||||
nscoord childContentBottom = 0;
|
||||
nscoord childContentBEnd = 0;
|
||||
WritingMode wm = child->GetWritingMode();
|
||||
if (!reflowNext && (skipIncremental || skipResizeHeightShrink)) {
|
||||
// This child does not need to be reflowed, but we may need to move it
|
||||
MoveChildTo(this, child, childOrigin);
|
||||
|
@ -508,7 +509,7 @@ nsColumnSetFrame::ReflowChildren(nsHTMLReflowMetrics& aDesiredSize,
|
|||
} else {
|
||||
aStatus = mLastFrameStatus;
|
||||
}
|
||||
childContentBottom = nsLayoutUtils::CalculateContentBottom(child);
|
||||
childContentBEnd = nsLayoutUtils::CalculateContentBEnd(wm, child);
|
||||
#ifdef DEBUG_roc
|
||||
printf("*** Skipping child #%d %p (incremental %d, resize height shrink %d): status = %d\n",
|
||||
columnCount, (void*)child, skipIncremental, skipResizeHeightShrink, aStatus);
|
||||
|
@ -547,8 +548,7 @@ nsColumnSetFrame::ReflowChildren(nsHTMLReflowMetrics& aDesiredSize,
|
|||
kidReflowState.mFlags.mNextInFlowUntouched = true;
|
||||
}
|
||||
|
||||
nsHTMLReflowMetrics kidDesiredSize(aReflowState.GetWritingMode(),
|
||||
aDesiredSize.mFlags);
|
||||
nsHTMLReflowMetrics kidDesiredSize(wm, aDesiredSize.mFlags);
|
||||
|
||||
// XXX it would be cool to consult the float manager for the
|
||||
// previous block to figure out the region of floats from the
|
||||
|
@ -578,12 +578,12 @@ nsColumnSetFrame::ReflowChildren(nsHTMLReflowMetrics& aDesiredSize,
|
|||
FinishReflowChild(child, PresContext(), kidDesiredSize,
|
||||
&kidReflowState, childOrigin.x, childOrigin.y, 0);
|
||||
|
||||
childContentBottom = nsLayoutUtils::CalculateContentBottom(child);
|
||||
if (childContentBottom > aConfig.mColMaxHeight) {
|
||||
childContentBEnd = nsLayoutUtils::CalculateContentBEnd(wm, child);
|
||||
if (childContentBEnd > aConfig.mColMaxHeight) {
|
||||
allFit = false;
|
||||
}
|
||||
if (childContentBottom > availSize.height) {
|
||||
aColData.mMaxOverflowingHeight = std::max(childContentBottom,
|
||||
if (childContentBEnd > availSize.height) {
|
||||
aColData.mMaxOverflowingHeight = std::max(childContentBEnd,
|
||||
aColData.mMaxOverflowingHeight);
|
||||
}
|
||||
}
|
||||
|
@ -591,9 +591,9 @@ nsColumnSetFrame::ReflowChildren(nsHTMLReflowMetrics& aDesiredSize,
|
|||
contentRect.UnionRect(contentRect, child->GetRect());
|
||||
|
||||
ConsiderChildOverflow(overflowRects, child);
|
||||
contentBottom = std::max(contentBottom, childContentBottom);
|
||||
aColData.mLastHeight = childContentBottom;
|
||||
aColData.mSumHeight += childContentBottom;
|
||||
contentBEnd = std::max(contentBEnd, childContentBEnd);
|
||||
aColData.mLastHeight = childContentBEnd;
|
||||
aColData.mSumHeight += childContentBEnd;
|
||||
|
||||
// Build a continuation column if necessary
|
||||
nsIFrame* kidNextInFlow = child->GetNextInFlow();
|
||||
|
@ -637,8 +637,8 @@ nsColumnSetFrame::ReflowChildren(nsHTMLReflowMetrics& aDesiredSize,
|
|||
kidNextInFlow->RemoveStateBits(NS_FRAME_IS_OVERFLOW_CONTAINER);
|
||||
}
|
||||
|
||||
if ((contentBottom > aReflowState.ComputedMaxHeight() ||
|
||||
contentBottom > aReflowState.ComputedHeight()) &&
|
||||
if ((contentBEnd > aReflowState.ComputedMaxBSize() ||
|
||||
contentBEnd > aReflowState.ComputedBSize()) &&
|
||||
aConfig.mBalanceColCount < INT32_MAX) {
|
||||
// We overflowed vertically, but have not exceeded the number of
|
||||
// columns. We're going to go into overflow columns now, so balancing
|
||||
|
@ -702,8 +702,8 @@ nsColumnSetFrame::ReflowChildren(nsHTMLReflowMetrics& aDesiredSize,
|
|||
}
|
||||
}
|
||||
|
||||
aColData.mMaxHeight = contentBottom;
|
||||
contentRect.height = std::max(contentRect.height, contentBottom);
|
||||
aColData.mMaxHeight = contentBEnd;
|
||||
contentRect.height = std::max(contentRect.height, contentBEnd);
|
||||
mLastFrameStatus = aStatus;
|
||||
|
||||
// contentRect included the borderPadding.left,borderPadding.top of the child rects
|
||||
|
|
|
@ -373,7 +373,7 @@ nsFirstLetterFrame::DrainOverflowFrames(nsPresContext* aPresContext)
|
|||
}
|
||||
|
||||
nscoord
|
||||
nsFirstLetterFrame::GetBaseline() const
|
||||
nsFirstLetterFrame::GetLogicalBaseline(WritingMode aWritingMode) const
|
||||
{
|
||||
return mBaseline;
|
||||
}
|
||||
|
|
Некоторые файлы не были показаны из-за слишком большого количества измененных файлов Показать больше
Загрузка…
Ссылка в новой задаче