зеркало из https://github.com/mozilla/pjs.git
Merge mozilla-central into Places.
This commit is contained in:
Коммит
045565c2b6
|
@ -54,8 +54,10 @@
|
|||
NotificationController::NotificationController(nsDocAccessible* aDocument,
|
||||
nsIPresShell* aPresShell) :
|
||||
mObservingState(eNotObservingRefresh), mDocument(aDocument),
|
||||
mPresShell(aPresShell)
|
||||
mPresShell(aPresShell), mTreeConstructedState(eTreeConstructionPending)
|
||||
{
|
||||
// Schedule initial accessible tree construction.
|
||||
ScheduleProcessing();
|
||||
}
|
||||
|
||||
NotificationController::~NotificationController()
|
||||
|
@ -129,6 +131,10 @@ NotificationController::ScheduleContentInsertion(nsAccessible* aContainer,
|
|||
nsIContent* aStartChildNode,
|
||||
nsIContent* aEndChildNode)
|
||||
{
|
||||
// Ignore content insertions until we constructed accessible tree.
|
||||
if (mTreeConstructedState == eTreeConstructionPending)
|
||||
return;
|
||||
|
||||
nsRefPtr<ContentInsertion> insertion =
|
||||
new ContentInsertion(mDocument, aContainer, aStartChildNode, aEndChildNode);
|
||||
|
||||
|
@ -177,6 +183,15 @@ NotificationController::WillRefresh(mozilla::TimeStamp aTime)
|
|||
// insertions or generic notifications.
|
||||
mObservingState = eRefreshProcessingForUpdate;
|
||||
|
||||
// Initial accessible tree construction.
|
||||
if (mTreeConstructedState == eTreeConstructionPending) {
|
||||
mTreeConstructedState = eTreeConstructed;
|
||||
mDocument->CacheChildrenInSubtree(mDocument);
|
||||
|
||||
NS_ASSERTION(mContentInsertions.Length() == 0,
|
||||
"Pending content insertions while initial accessible tree isn't created!");
|
||||
}
|
||||
|
||||
// Process content inserted notifications to update the tree. Process other
|
||||
// notifications like DOM events and then flush event queue. If any new
|
||||
// notifications are queued during this processing then they will be processed
|
||||
|
|
|
@ -256,6 +256,17 @@ private:
|
|||
*/
|
||||
nsIPresShell* mPresShell;
|
||||
|
||||
/**
|
||||
* Indicate whether initial construction of the document's accessible tree
|
||||
* performed or pending. When the document accessible is created then
|
||||
* we construct its initial accessible tree.
|
||||
*/
|
||||
enum eTreeConstructedState {
|
||||
eTreeConstructed,
|
||||
eTreeConstructionPending
|
||||
};
|
||||
eTreeConstructedState mTreeConstructedState;
|
||||
|
||||
/**
|
||||
* Storage for content inserted notification information.
|
||||
*/
|
||||
|
|
|
@ -56,6 +56,9 @@
|
|||
<emItem id="{3f963a5b-e555-4543-90e2-c3908898db71}">
|
||||
<versionRange minVersion=" " maxVersion="8.5"/>
|
||||
</emItem>
|
||||
<emItem id="{46551EC9-40F0-4e47-8E18-8E5CF550CFB8}">
|
||||
<versionRange minVersion="1.1b1" maxVersion="1.1b1"/>
|
||||
</emItem>
|
||||
<emItem id="{4B3803EA-5230-4DC3-A7FC-33638F3D3542}">
|
||||
<versionRange minVersion="1.2" maxVersion="1.2">
|
||||
<targetApplication id="{ec8030f7-c20a-464f-9b0e-13a3a9e97384}">
|
||||
|
@ -64,8 +67,11 @@
|
|||
</versionRange>
|
||||
</emItem>
|
||||
<emItem id="{8CE11043-9A15-4207-A565-0C94C42D590D}"/>
|
||||
<emItem id="{AB2CE124-6272-4b12-94A9-7303C7397BD1}">
|
||||
<versionRange severity="1"/>
|
||||
</emItem>
|
||||
<emItem id="{B13721C7-F507-4982-B2E5-502A71474FED}">
|
||||
<versionRange minVersion=" " maxVersion="3.3.0.3970" severity="1"/>
|
||||
<versionRange severity="1"/>
|
||||
</emItem>
|
||||
<emItem id="{E8E88AB0-7182-11DF-904E-6045E0D72085}"/>
|
||||
</emItems>
|
||||
|
|
|
@ -39,17 +39,20 @@
|
|||
# ***** END LICENSE BLOCK *****
|
||||
|
||||
<menupopup id="appmenu-popup"
|
||||
onpopupshowing="if (event.target == this) {
|
||||
updateEditUIVisibility();
|
||||
#ifdef MOZ_SERVICES_SYNC
|
||||
onpopupshowing="updateEditUIVisibility();gSyncUI.updateUI();">
|
||||
#else
|
||||
onpopupshowing="updateEditUIVisibility();">
|
||||
gSyncUI.updateUI();
|
||||
#endif
|
||||
return;
|
||||
}
|
||||
if (event.target.parentNode.parentNode.parentNode.parentNode == this)
|
||||
this._currentPopup = event.target;">
|
||||
<hbox>
|
||||
<vbox id="appmenuPrimaryPane">
|
||||
<splitmenu id="appmenu_newTab"
|
||||
label="&tabCmd.label;"
|
||||
command="cmd_newNavigatorTab"
|
||||
key="key_newNavigatorTab">
|
||||
command="cmd_newNavigatorTab">
|
||||
<menupopup>
|
||||
<menuitem id="appmenu_newTab_popup"
|
||||
label="&tabCmd.label;"
|
||||
|
@ -116,8 +119,7 @@
|
|||
<splitmenu id="appmenu_print"
|
||||
iconic="true"
|
||||
label="&printCmd.label;"
|
||||
command="cmd_print"
|
||||
key="printKb">
|
||||
command="cmd_print">
|
||||
<menupopup>
|
||||
<menuitem id="appmenu_print_popup"
|
||||
class="menuitem-iconic"
|
||||
|
@ -203,8 +205,7 @@
|
|||
<splitmenu id="appmenu_bookmarks"
|
||||
iconic="true"
|
||||
label="&bookmarksMenu.label;"
|
||||
command="Browser:ShowAllBookmarks"
|
||||
key="manBookmarkKb">
|
||||
command="Browser:ShowAllBookmarks">
|
||||
<menupopup id="appmenu_bookmarksPopup"
|
||||
placespopup="true"
|
||||
context="placesContext"
|
||||
|
@ -267,8 +268,7 @@
|
|||
<splitmenu id="appmenu_history"
|
||||
iconic="true"
|
||||
label="&historyMenu.label;"
|
||||
command="Browser:ShowAllHistory"
|
||||
key="showAllHistoryKb">
|
||||
command="Browser:ShowAllHistory">
|
||||
<menupopup id="appmenu_historyMenupopup"
|
||||
placespopup="true"
|
||||
oncommand="this.parentNode._placesView._onCommand(event);"
|
||||
|
|
|
@ -348,10 +348,8 @@ var PlacesCommandHook = {
|
|||
if (starIcon && isElementVisible(starIcon)) {
|
||||
// Make sure the bookmark properties dialog hangs toward the middle of
|
||||
// the location bar in RTL builds
|
||||
var position = (getComputedStyle(gNavToolbox, "").direction == "rtl") ?
|
||||
'bottomcenter topleft' : 'bottomcenter topright';
|
||||
if (aShowEditUI)
|
||||
StarUI.showEditBookmarkPopup(itemId, starIcon, position);
|
||||
StarUI.showEditBookmarkPopup(itemId, starIcon, "bottomcenter topright");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -155,13 +155,18 @@ splitmenu {
|
|||
-moz-binding: url("chrome://browser/content/urlbarBindings.xml#splitmenu");
|
||||
}
|
||||
|
||||
.split-menuitem-item {
|
||||
.splitmenu-menuitem {
|
||||
-moz-binding: url("chrome://global/content/bindings/menu.xml#menuitem");
|
||||
list-style-image: inherit;
|
||||
-moz-image-region: inherit;
|
||||
}
|
||||
|
||||
.split-menuitem-menu > .menu-text,
|
||||
.split-menuitem-menu > .menu-accel-container {
|
||||
.splitmenu-menuitem[iconic="true"] {
|
||||
-moz-binding: url("chrome://global/content/bindings/menu.xml#menuitem-iconic");
|
||||
}
|
||||
|
||||
.splitmenu-menu > .menu-text,
|
||||
:-moz-any(.splitmenu-menu, .splitmenu-menuitem) > .menu-accel-container {
|
||||
display: none;
|
||||
}
|
||||
|
||||
|
@ -279,7 +284,7 @@ panel[noactions] > richlistbox > richlistitem[type~="action"] > .ac-url-box > .a
|
|||
visibility: visible;
|
||||
}
|
||||
|
||||
#urlbar:not([actiontype]) > #urlbar-display {
|
||||
#urlbar:not([actiontype]) > #urlbar-display-box {
|
||||
display: none;
|
||||
}
|
||||
|
||||
|
|
|
@ -571,7 +571,9 @@
|
|||
</hbox>
|
||||
</hbox>
|
||||
</box>
|
||||
<label id="urlbar-display" value="&urlbar.switchToTab.label;"/>
|
||||
<box id="urlbar-display-box" align="center">
|
||||
<label id="urlbar-display" value="&urlbar.switchToTab.label;"/>
|
||||
</box>
|
||||
<hbox id="urlbar-icons">
|
||||
<image id="page-report-button"
|
||||
class="urlbar-icon"
|
||||
|
|
|
@ -24,6 +24,7 @@
|
|||
* Michael Yoshitaka Erlewine <mitcho@mitcho.com>
|
||||
* Ehsan Akhgari <ehsan@mozilla.com>
|
||||
* Raymond Lee <raymond@appcoast.com>
|
||||
* Tim Taubert <tim.taubert@gmx.de>
|
||||
*
|
||||
* Alternatively, the contents of this file may be used under the terms of
|
||||
* either the GNU General Public License Version 2 or later (the "GPL"), or
|
||||
|
@ -1480,8 +1481,15 @@ GroupItem.prototype = Utils.extend(new Item(), new Subscribable(), {
|
|||
addTab: drag.info.item.parent != self,
|
||||
animate: true});
|
||||
}
|
||||
|
||||
// remove the item from its parent if that's not the current groupItem.
|
||||
// this may occur when dragging too quickly so the out event is not fired.
|
||||
var groupItem = drag.info.item.parent;
|
||||
if (groupItem && self !== groupItem)
|
||||
groupItem.remove(drag.info.$el, {dontClose: true});
|
||||
|
||||
if (dropIndex !== false)
|
||||
options = {index: dropIndex}
|
||||
options = {index: dropIndex};
|
||||
this.add(drag.info.$el, options);
|
||||
GroupItems.setActiveGroupItem(this);
|
||||
dropIndex = false;
|
||||
|
@ -1558,34 +1566,21 @@ GroupItem.prototype = Utils.extend(new Item(), new Subscribable(), {
|
|||
// Reorders the tabs in the tab bar based on the arrangment of the tabs
|
||||
// shown in the groupItem.
|
||||
reorderTabsBasedOnTabItemOrder: function GroupItem_reorderTabsBasedOnTabItemOrder() {
|
||||
let targetIndices = null;
|
||||
let indices;
|
||||
let tabs = this._children.map(function (tabItem) tabItem.tab);
|
||||
|
||||
let self = this;
|
||||
this._children.some(function(tabItem, index) {
|
||||
// if no targetIndices, or it was reset, recompute
|
||||
if (!targetIndices) {
|
||||
let currentIndices = [tabItem.tab._tPos for each (tabItem in self._children)];
|
||||
targetIndices = currentIndices.concat().sort();
|
||||
// if no resorting is required, we're done!
|
||||
if (currentIndices == targetIndices)
|
||||
return true;
|
||||
}
|
||||
|
||||
// Compute a target range for this tab's index
|
||||
let originalIndex = tabItem.tab._tPos;
|
||||
let targetRange = new Range(index ? targetIndices[index - 1] : -1,
|
||||
targetIndices[index + 1] || Infinity);
|
||||
tabs.forEach(function (tab, index) {
|
||||
if (!indices)
|
||||
indices = tabs.map(function (tab) tab._tPos);
|
||||
|
||||
// If the originalIndex of this tab is not within its target,
|
||||
// let's move it to the targetIndex.
|
||||
if (!targetRange.contains(originalIndex)) {
|
||||
let targetIndex = targetIndices[index];
|
||||
gBrowser.moveTabTo(tabItem.tab, targetIndex);
|
||||
// force recomputing targetIndices
|
||||
targetIndices = null;
|
||||
let start = index ? indices[index - 1] + 1 : 0;
|
||||
let end = index + 1 < indices.length ? indices[index + 1] - 1 : Infinity;
|
||||
let targetRange = new Range(start, end);
|
||||
|
||||
if (!targetRange.contains(tab._tPos)) {
|
||||
gBrowser.moveTabTo(tab, start);
|
||||
indices = null;
|
||||
}
|
||||
|
||||
return false;
|
||||
});
|
||||
},
|
||||
|
||||
|
|
|
@ -24,6 +24,7 @@
|
|||
* Michael Yoshitaka Erlewine <mitcho@mitcho.com>
|
||||
* Ehsan Akhgari <ehsan@mozilla.com>
|
||||
* Raymond Lee <raymond@appcoast.com>
|
||||
* Tim Taubert <tim.taubert@gmx.de>
|
||||
*
|
||||
* Alternatively, the contents of this file may be used under the terms of
|
||||
* either the GNU General Public License Version 2 or later (the "GPL"), or
|
||||
|
@ -62,7 +63,7 @@ function TabItem(tab, options) {
|
|||
var $div = iQ('<div>')
|
||||
.addClass('tab')
|
||||
.html("<div class='thumb'>" +
|
||||
"<img class='cached-thumb' style='display:none'/><canvas moz-opaque='true'/></div>" +
|
||||
"<img class='cached-thumb' style='display:none'/><canvas moz-opaque/></div>" +
|
||||
"<div class='favicon'><img/></div>" +
|
||||
"<span class='tab-title'> </span>"
|
||||
)
|
||||
|
@ -808,7 +809,7 @@ let TabItems = {
|
|||
this.minTabHeight = this.minTabWidth * this.tabHeight / this.tabWidth;
|
||||
|
||||
let $canvas = iQ("<canvas>")
|
||||
.attr('moz-opaque', true);
|
||||
.attr('moz-opaque', '');
|
||||
$canvas.appendTo(iQ("body"));
|
||||
$canvas.hide();
|
||||
this.tempCanvas = $canvas[0];
|
||||
|
|
|
@ -200,11 +200,6 @@ let UI = {
|
|||
}
|
||||
});
|
||||
|
||||
iQ(window).bind("beforeunload", function() {
|
||||
Array.forEach(gBrowser.tabs, function(tab) {
|
||||
gBrowser.showTab(tab);
|
||||
});
|
||||
});
|
||||
iQ(window).bind("unload", function() {
|
||||
self.uninit();
|
||||
});
|
||||
|
|
|
@ -110,14 +110,6 @@ function test_blocked_install() {
|
|||
"software on your computer.",
|
||||
"Should have seen the right message");
|
||||
|
||||
// Click on Allow
|
||||
EventUtils.synthesizeMouse(notification.button, 20, 10, {});
|
||||
|
||||
// Notification should have changed to progress notification
|
||||
ok(PopupNotifications.isPanelOpen, "Notification should still be open");
|
||||
notification = aPanel.childNodes[0];
|
||||
is(notification.id, "addon-progress-notification", "Should have seen the progress notification");
|
||||
|
||||
// Wait for the install confirmation dialog
|
||||
wait_for_install_dialog(function(aWindow) {
|
||||
// Wait for the complete notification
|
||||
|
@ -140,6 +132,15 @@ function test_blocked_install() {
|
|||
|
||||
aWindow.document.documentElement.acceptDialog();
|
||||
});
|
||||
|
||||
// Click on Allow
|
||||
EventUtils.synthesizeMouse(notification.button, 20, 10, {});
|
||||
|
||||
// Notification should have changed to progress notification
|
||||
ok(PopupNotifications.isPanelOpen, "Notification should still be open");
|
||||
notification = aPanel.childNodes[0];
|
||||
is(notification.id, "addon-progress-notification", "Should have seen the progress notification");
|
||||
|
||||
});
|
||||
|
||||
var triggers = encodeURIComponent(JSON.stringify({
|
||||
|
@ -705,15 +706,6 @@ function test_cancel_restart() {
|
|||
notification = aPanel.childNodes[0];
|
||||
is(notification.id, "addon-install-cancelled-notification", "Should have seen the cancelled notification");
|
||||
|
||||
// Restart the download
|
||||
EventUtils.synthesizeMouse(notification.button, 20, 10, {});
|
||||
|
||||
// Should be back to a progress notification
|
||||
ok(PopupNotifications.isPanelOpen, "Notification should still be open");
|
||||
is(PopupNotifications.panel.childNodes.length, 1, "Should be only one notification");
|
||||
notification = aPanel.childNodes[0];
|
||||
is(notification.id, "addon-progress-notification", "Should have seen the progress notification");
|
||||
|
||||
// Wait for the install confirmation dialog
|
||||
wait_for_install_dialog(function(aWindow) {
|
||||
// Wait for the complete notification
|
||||
|
@ -737,6 +729,16 @@ function test_cancel_restart() {
|
|||
|
||||
aWindow.document.documentElement.acceptDialog();
|
||||
});
|
||||
|
||||
// Restart the download
|
||||
EventUtils.synthesizeMouse(notification.button, 20, 10, {});
|
||||
|
||||
// Should be back to a progress notification
|
||||
ok(PopupNotifications.isPanelOpen, "Notification should still be open");
|
||||
is(PopupNotifications.panel.childNodes.length, 1, "Should be only one notification");
|
||||
notification = aPanel.childNodes[0];
|
||||
is(notification.id, "addon-progress-notification", "Should have seen the progress notification");
|
||||
|
||||
});
|
||||
});
|
||||
|
||||
|
|
|
@ -72,6 +72,7 @@ _BROWSER_FILES = \
|
|||
browser_tabview_bug598600.js \
|
||||
browser_tabview_bug599626.js \
|
||||
browser_tabview_bug600645.js \
|
||||
browser_tabview_bug600812.js \
|
||||
browser_tabview_bug604098.js \
|
||||
browser_tabview_bug606657.js \
|
||||
browser_tabview_bug606905.js \
|
||||
|
@ -79,6 +80,7 @@ _BROWSER_FILES = \
|
|||
browser_tabview_bug608184.js \
|
||||
browser_tabview_bug608158.js \
|
||||
browser_tabview_bug610242.js \
|
||||
browser_tabview_bug616729.js \
|
||||
browser_tabview_bug616967.js \
|
||||
browser_tabview_bug618828.js \
|
||||
browser_tabview_bug619937.js \
|
||||
|
|
|
@ -83,10 +83,10 @@ function onTabViewWindowHidden() {
|
|||
|
||||
is(moves, 1, "Only one move should be necessary for this basic move.");
|
||||
|
||||
is(originalTab._tPos, 1, "Original tab is in position 0");
|
||||
is(newTabs[0]._tPos, 2, "Robots is in position 1");
|
||||
is(newTabs[1]._tPos, 3, "Mozilla is in position 2");
|
||||
is(newTabs[2]._tPos, 0, "Credits is in position 3");
|
||||
is(newTabs[2]._tPos, 0, "Credits is in position 0");
|
||||
is(originalTab._tPos, 1, "Original tab is in position 1");
|
||||
is(newTabs[0]._tPos, 2, "Robots is in position 2");
|
||||
is(newTabs[1]._tPos, 3, "Mozilla is in position 3");
|
||||
|
||||
gBrowser.removeTab(newTabs[0]);
|
||||
gBrowser.removeTab(newTabs[1]);
|
||||
|
|
|
@ -0,0 +1,79 @@
|
|||
/* ***** BEGIN LICENSE BLOCK *****
|
||||
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
||||
*
|
||||
* The contents of this file are subject to the Mozilla Public License Version
|
||||
* 1.1 (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
* http://www.mozilla.org/MPL/
|
||||
*
|
||||
* Software distributed under the License is distributed on an "AS IS" basis,
|
||||
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
||||
* for the specific language governing rights and limitations under the
|
||||
* License.
|
||||
*
|
||||
* The Original Code is a test for bug 600812.
|
||||
*
|
||||
* The Initial Developer of the Original Code is
|
||||
* Mozilla Foundation.
|
||||
* Portions created by the Initial Developer are Copyright (C) 2010
|
||||
* the Initial Developer. All Rights Reserved.
|
||||
*
|
||||
* Contributor(s):
|
||||
* Tim Taubert <tim.taubert@gmx.de>
|
||||
*
|
||||
* Alternatively, the contents of this file may be used under the terms of
|
||||
* either the GNU General Public License Version 2 or later (the "GPL"), or
|
||||
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
||||
* in which case the provisions of the GPL or the LGPL are applicable instead
|
||||
* of those above. If you wish to allow use of your version of this file only
|
||||
* under the terms of either the GPL or the LGPL, and not to allow others to
|
||||
* use your version of this file under the terms of the MPL, indicate your
|
||||
* decision by deleting the provisions above and replace them with the notice
|
||||
* and other provisions required by the GPL or the LGPL. If you do not delete
|
||||
* the provisions above, a recipient may use your version of this file under
|
||||
* the terms of any one of the MPL, the GPL or the LGPL.
|
||||
*
|
||||
* ***** END LICENSE BLOCK ***** */
|
||||
|
||||
function test() {
|
||||
let cw;
|
||||
|
||||
let createGroupItem = function () {
|
||||
let bounds = new cw.Rect(20, 20, 150, 150);
|
||||
let groupItem = new cw.GroupItem([], {bounds: bounds, immediately: true});
|
||||
|
||||
cw.GroupItems.setActiveGroupItem(groupItem);
|
||||
gBrowser.loadOneTab('about:blank', {inBackground: true});
|
||||
|
||||
return groupItem;
|
||||
}
|
||||
|
||||
let testVeryQuickDragAndDrop = function () {
|
||||
let sourceGroup = cw.GroupItems.groupItems[0];
|
||||
let targetGroup = createGroupItem();
|
||||
|
||||
sourceGroup.pushAway(true);
|
||||
targetGroup.pushAway(true);
|
||||
|
||||
let sourceTab = sourceGroup.getChild(0).container;
|
||||
EventUtils.synthesizeMouseAtCenter(sourceTab, {type: 'mousedown'}, cw);
|
||||
|
||||
let targetTab = targetGroup.getChild(0).container;
|
||||
EventUtils.synthesizeMouseAtCenter(targetTab, {type: 'mousemove'}, cw);
|
||||
EventUtils.synthesizeMouseAtCenter(targetTab, {type: 'mouseup'}, cw);
|
||||
|
||||
is(targetGroup.getChildren().length, 2, 'target group has two tabs');
|
||||
is(cw.GroupItems.groupItems.length, 1, 'sourceGroup was closed');
|
||||
isnot(cw.GroupItems.groupItems[0], sourceGroup, 'sourceGroup was closed');
|
||||
|
||||
targetGroup.getChild(0).close();
|
||||
hideTabView(finish);
|
||||
}
|
||||
|
||||
waitForExplicitFinish();
|
||||
|
||||
showTabView(function () {
|
||||
cw = TabView.getContentWindow();
|
||||
afterAllTabsLoaded(testVeryQuickDragAndDrop);
|
||||
});
|
||||
}
|
|
@ -0,0 +1,157 @@
|
|||
/* ***** BEGIN LICENSE BLOCK *****
|
||||
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
||||
*
|
||||
* The contents of this file are subject to the Mozilla Public License Version
|
||||
* 1.1 (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
* http://www.mozilla.org/MPL/
|
||||
*
|
||||
* Software distributed under the License is distributed on an "AS IS" basis,
|
||||
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
||||
* for the specific language governing rights and limitations under the
|
||||
* License.
|
||||
*
|
||||
* The Original Code is a test for bug 616729.
|
||||
*
|
||||
* The Initial Developer of the Original Code is
|
||||
* Mozilla Foundation.
|
||||
* Portions created by the Initial Developer are Copyright (C) 2010
|
||||
* the Initial Developer. All Rights Reserved.
|
||||
*
|
||||
* Contributor(s):
|
||||
* Tim Taubert <tim.taubert@gmx.de>
|
||||
*
|
||||
* Alternatively, the contents of this file may be used under the terms of
|
||||
* either the GNU General Public License Version 2 or later (the "GPL"), or
|
||||
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
||||
* in which case the provisions of the GPL or the LGPL are applicable instead
|
||||
* of those above. If you wish to allow use of your version of this file only
|
||||
* under the terms of either the GPL or the LGPL, and not to allow others to
|
||||
* use your version of this file under the terms of the MPL, indicate your
|
||||
* decision by deleting the provisions above and replace them with the notice
|
||||
* and other provisions required by the GPL or the LGPL. If you do not delete
|
||||
* the provisions above, a recipient may use your version of this file under
|
||||
* the terms of any one of the MPL, the GPL or the LGPL.
|
||||
*
|
||||
* ***** END LICENSE BLOCK ***** */
|
||||
|
||||
function test() {
|
||||
let cw;
|
||||
|
||||
let createGroupItem = function () {
|
||||
let bounds = new cw.Rect(20, 20, 400, 200);
|
||||
let groupItem = new cw.GroupItem([], {bounds: bounds, immediately: true});
|
||||
cw.GroupItems.setActiveGroupItem(groupItem);
|
||||
|
||||
for (let i=0; i<5; i++)
|
||||
gBrowser.loadOneTab('about:blank', {inBackground: true});
|
||||
|
||||
return groupItem;
|
||||
}
|
||||
|
||||
let assertCorrectItemOrder = function (items) {
|
||||
for (let i=1; i<items.length; i++) {
|
||||
if (items[i-1].tab._tPos > items[i].tab._tPos) {
|
||||
ok(false, 'tabs were correctly reordered');
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let testVariousTabOrders = function () {
|
||||
let groupItem = createGroupItem();
|
||||
let [tab1, tab2, tab3, tab4, tab5] = groupItem.getChildren();
|
||||
|
||||
// prepare tests
|
||||
let tests = [];
|
||||
tests.push([tab1, tab2, tab3, tab4, tab5]);
|
||||
tests.push([tab5, tab4, tab3, tab2, tab1]);
|
||||
tests.push([tab1, tab2, tab3, tab4]);
|
||||
tests.push([tab4, tab3, tab2, tab1]);
|
||||
tests.push([tab1, tab2, tab3]);
|
||||
tests.push([tab1, tab2, tab3]);
|
||||
tests.push([tab1, tab3, tab2]);
|
||||
tests.push([tab2, tab1, tab3]);
|
||||
tests.push([tab2, tab3, tab1]);
|
||||
tests.push([tab3, tab1, tab2]);
|
||||
tests.push([tab3, tab2, tab1]);
|
||||
tests.push([tab1, tab2]);
|
||||
tests.push([tab2, tab1]);
|
||||
tests.push([tab1]);
|
||||
|
||||
// test reordering of empty groups - removes the last tab and causes
|
||||
// the groupItem to close
|
||||
tests.push([]);
|
||||
|
||||
while (tests.length) {
|
||||
let test = tests.shift();
|
||||
|
||||
// prepare
|
||||
let items = groupItem.getChildren();
|
||||
while (test.length < items.length)
|
||||
items[items.length-1].close();
|
||||
|
||||
let orig = cw.Utils.copy(items);
|
||||
items.sort(function (a, b) test.indexOf(a) - test.indexOf(b));
|
||||
|
||||
// order and check
|
||||
groupItem.reorderTabsBasedOnTabItemOrder();
|
||||
assertCorrectItemOrder(items);
|
||||
|
||||
// revert to original item order
|
||||
items.sort(function (a, b) orig.indexOf(a) - orig.indexOf(b));
|
||||
groupItem.reorderTabsBasedOnTabItemOrder();
|
||||
}
|
||||
|
||||
testMoveBetweenGroups();
|
||||
}
|
||||
|
||||
let testMoveBetweenGroups = function () {
|
||||
let groupItem = createGroupItem();
|
||||
let groupItem2 = createGroupItem();
|
||||
|
||||
afterAllTabsLoaded(function () {
|
||||
// move item from group1 to group2
|
||||
let child = groupItem.getChild(2);
|
||||
groupItem.remove(child);
|
||||
|
||||
groupItem2.add(child, {index: 3});
|
||||
groupItem2.reorderTabsBasedOnTabItemOrder();
|
||||
|
||||
assertCorrectItemOrder(groupItem.getChildren());
|
||||
assertCorrectItemOrder(groupItem2.getChildren());
|
||||
|
||||
// move item from group2 to group1
|
||||
child = groupItem2.getChild(1);
|
||||
groupItem2.remove(child);
|
||||
|
||||
groupItem.add(child, {index: 1});
|
||||
groupItem.reorderTabsBasedOnTabItemOrder();
|
||||
|
||||
assertCorrectItemOrder(groupItem.getChildren());
|
||||
assertCorrectItemOrder(groupItem2.getChildren());
|
||||
|
||||
// cleanup
|
||||
groupItem.addSubscriber(groupItem, 'groupHidden', function () {
|
||||
groupItem.removeSubscriber(groupItem, 'groupHidden');
|
||||
groupItem.closeHidden();
|
||||
groupItem2.closeAll();
|
||||
});
|
||||
|
||||
groupItem2.addSubscriber(groupItem, 'groupHidden', function () {
|
||||
groupItem2.removeSubscriber(groupItem, 'groupHidden');
|
||||
groupItem2.closeHidden();
|
||||
hideTabView(finish);
|
||||
});
|
||||
|
||||
groupItem.closeAll();
|
||||
});
|
||||
}
|
||||
|
||||
waitForExplicitFinish();
|
||||
|
||||
showTabView(function () {
|
||||
cw = TabView.getContentWindow();
|
||||
afterAllTabsLoaded(testVariousTabOrders);
|
||||
});
|
||||
}
|
|
@ -783,7 +783,8 @@
|
|||
overLink.style.maxWidth = maxWidth + "px";
|
||||
}
|
||||
|
||||
this._originLabel.value = this.value;
|
||||
var action = this._parseActionUrl(this._value);
|
||||
this._originLabel.value = action ? action.param : this._value;
|
||||
]]></body>
|
||||
</method>
|
||||
|
||||
|
@ -1298,27 +1299,85 @@
|
|||
|
||||
<binding id="splitmenu">
|
||||
<content>
|
||||
<xul:menuitem anonid="item" flex="1"
|
||||
class="menuitem-tooltip split-menuitem-item"
|
||||
xbl:inherits="label,key"/>
|
||||
<xul:menu anonid="menu" class="split-menuitem-menu"
|
||||
<xul:hbox anonid="menuitem" flex="1"
|
||||
class="splitmenu-menuitem"
|
||||
xbl:inherits="iconic,label,disabled,onclick=oncommand,_moz-menuactive=active"/>
|
||||
<xul:menu anonid="menu" class="splitmenu-menu"
|
||||
xbl:inherits="disabled"
|
||||
oncommand="event.stopPropagation();">
|
||||
<children includes="menupopup"/>
|
||||
</xul:menu>
|
||||
</content>
|
||||
|
||||
<implementation>
|
||||
<constructor><![CDATA[
|
||||
if (this.getAttribute("iconic") == "true") {
|
||||
this.item.classList.remove("menuitem-tooltip");
|
||||
this.item.classList.add("menuitem-iconic-tooltip");
|
||||
this.item.classList.add("menuitem-iconic");
|
||||
}
|
||||
]]></constructor>
|
||||
<field name="item" readonly="true">
|
||||
document.getAnonymousElementByAttribute(this, "anonid", "item");
|
||||
<field name="menuitem" readonly="true">
|
||||
document.getAnonymousElementByAttribute(this, "anonid", "menuitem");
|
||||
</field>
|
||||
<field name="menu" readonly="true">
|
||||
document.getAnonymousElementByAttribute(this, "anonid", "menu");
|
||||
</field>
|
||||
|
||||
<field name="_menuDelay">600</field>
|
||||
|
||||
<field name="_parentMenupopup"><![CDATA[
|
||||
let node = this.parentNode;
|
||||
while (node) {
|
||||
if (node.localName == "menupopup")
|
||||
break;
|
||||
node = node.parentNode;
|
||||
}
|
||||
node;
|
||||
]]></field>
|
||||
</implementation>
|
||||
|
||||
<handlers>
|
||||
<handler event="mouseover"><![CDATA[
|
||||
if (this.getAttribute("active") != "true" &&
|
||||
this.getAttribute("disabled") != "true") {
|
||||
if (this._parentMenupopup._currentPopup)
|
||||
this._parentMenupopup._currentPopup.hidePopup();
|
||||
|
||||
this.setAttribute("active", "true");
|
||||
|
||||
let self = this;
|
||||
setTimeout(function () {
|
||||
if (self.getAttribute("active") == "true")
|
||||
self.menu.open = true;
|
||||
}, this._menuDelay);
|
||||
}
|
||||
]]></handler>
|
||||
|
||||
<handler event="mouseout"><![CDATA[
|
||||
if (this.menu.open)
|
||||
return;
|
||||
|
||||
let node = event.relatedTarget;
|
||||
while (node) {
|
||||
if (node == this)
|
||||
return;
|
||||
node = node.parentNode;
|
||||
}
|
||||
this.removeAttribute("active");
|
||||
]]></handler>
|
||||
|
||||
<handler event="popuphidden"><![CDATA[
|
||||
if (event.target == this.firstChild)
|
||||
this.removeAttribute("active");
|
||||
]]></handler>
|
||||
|
||||
<handler event="click" phase="capturing"><![CDATA[
|
||||
let node = event.originalTarget;
|
||||
while (true) {
|
||||
if (node == this.menuitem)
|
||||
break;
|
||||
if (node == this)
|
||||
return;
|
||||
node = node.parentNode;
|
||||
}
|
||||
|
||||
this._parentMenupopup.hidePopup();
|
||||
]]></handler>
|
||||
</handlers>
|
||||
</binding>
|
||||
|
||||
<binding id="menuitem-tooltip" extends="chrome://global/content/bindings/menu.xml#menuitem">
|
||||
|
@ -1342,5 +1401,5 @@
|
|||
]]></constructor>
|
||||
</implementation>
|
||||
</binding>
|
||||
|
||||
|
||||
</bindings>
|
||||
|
|
|
@ -943,17 +943,19 @@ toolbar[iconsize="small"] #feed-button {
|
|||
-moz-margin-start: 0;
|
||||
}
|
||||
|
||||
#urlbar-display {
|
||||
margin-top: -2px;
|
||||
margin-bottom: -2px;
|
||||
padding-top: 3px;
|
||||
padding-bottom: 2px;
|
||||
-moz-padding-end: 3px;
|
||||
color: GrayText;
|
||||
#urlbar-display-box {
|
||||
margin-top: -1px;
|
||||
margin-bottom: -1px;
|
||||
-moz-border-end: 1px solid #AAA;
|
||||
-moz-margin-end: 3px;
|
||||
}
|
||||
|
||||
#urlbar-display {
|
||||
margin-top: 0;
|
||||
margin-bottom: 0;
|
||||
color: GrayText;
|
||||
}
|
||||
|
||||
#PopupAutoComplete,
|
||||
#PopupAutoCompleteRichResult {
|
||||
direction: ltr !important;
|
||||
|
|
|
@ -886,17 +886,17 @@ toolbar[mode="icons"] #zoom-in-button {
|
|||
max-width: 20em;
|
||||
}
|
||||
|
||||
#urlbar-display {
|
||||
margin-top: -3px;
|
||||
margin-bottom: -2px;
|
||||
padding-top: 3px;
|
||||
padding-bottom: 2px;
|
||||
-moz-padding-end: 3px;
|
||||
color: GrayText;
|
||||
#urlbar-display-box {
|
||||
-moz-border-end: 1px solid #AAA;
|
||||
-moz-margin-end: 3px;
|
||||
}
|
||||
|
||||
#urlbar-display {
|
||||
margin-top: 0;
|
||||
margin-bottom: 0;
|
||||
color: GrayText;
|
||||
}
|
||||
|
||||
#PopupAutoCompleteRichResult {
|
||||
direction: ltr !important;
|
||||
margin-top: 2px;
|
||||
|
@ -1421,12 +1421,12 @@ sidebarheader {
|
|||
-moz-border-end: 1px solid #404040;
|
||||
min-width: 1px;
|
||||
width: 1px;
|
||||
background-image: none !important;
|
||||
}
|
||||
background-image: none !important;
|
||||
}
|
||||
|
||||
#sidebar-title {
|
||||
color: #535f6d;
|
||||
font-weight: bold;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
#sidebar-throbber[loading="true"] {
|
||||
|
|
|
@ -219,7 +219,7 @@
|
|||
-moz-margin-start: .5em;
|
||||
}
|
||||
|
||||
.split-menuitem-menu {
|
||||
.splitmenu-menu {
|
||||
-moz-box-pack: end;
|
||||
}
|
||||
|
||||
|
@ -1128,17 +1128,19 @@ html|*.urlbar-input:-moz-lwtheme:-moz-placeholder,
|
|||
-moz-box-align: stretch;
|
||||
}
|
||||
|
||||
#urlbar-display {
|
||||
#urlbar-display-box {
|
||||
margin-top: -2px;
|
||||
margin-bottom: -2px;
|
||||
padding-top: 3px;
|
||||
padding-bottom: 2px;
|
||||
-moz-padding-end: 3px;
|
||||
color: GrayText;
|
||||
-moz-border-end: 1px solid #AAA;
|
||||
-moz-margin-end: 3px;
|
||||
}
|
||||
|
||||
#urlbar-display {
|
||||
margin-top: 0;
|
||||
margin-bottom: 0;
|
||||
color: GrayText;
|
||||
}
|
||||
|
||||
/* over-link in location bar */
|
||||
|
||||
.urlbar-origin-label {
|
||||
|
|
|
@ -45,6 +45,10 @@ include $(DEPTH)/config/autoconf.mk
|
|||
|
||||
MODULE = build
|
||||
|
||||
ifeq ($(USE_ELF_HACK)$(HOST_OS_ARCH)$(OS_ARCH)$(OS_TARGET),1LinuxLinuxLinux)
|
||||
DIRS = elfhack
|
||||
endif
|
||||
|
||||
include $(topsrcdir)/config/rules.mk
|
||||
|
||||
libs:: $(srcdir)/run-mozilla.sh
|
||||
|
|
|
@ -0,0 +1,112 @@
|
|||
#
|
||||
# ***** BEGIN LICENSE BLOCK *****
|
||||
# Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
||||
#
|
||||
# The contents of this file are subject to the Mozilla Public License Version
|
||||
# 1.1 (the "License"); you may not use this file except in compliance with
|
||||
# the License. You may obtain a copy of the License at
|
||||
# http://www.mozilla.org/MPL/
|
||||
#
|
||||
# Software distributed under the License is distributed on an "AS IS" basis,
|
||||
# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
||||
# for the specific language governing rights and limitations under the
|
||||
# License.
|
||||
#
|
||||
# The Original Code is elfhack
|
||||
#
|
||||
# The Initial Developer of the Original Code is
|
||||
# Mozilla Foundation
|
||||
# Portions created by the Initial Developer are Copyright (C) 2010
|
||||
# the Initial Developer. All Rights Reserved.
|
||||
#
|
||||
# Contributor(s):
|
||||
# Mike Hommey <mh@glandium.org>
|
||||
#
|
||||
# Alternatively, the contents of this file may be used under the terms of
|
||||
# either of the GNU General Public License Version 2 or later (the "GPL"),
|
||||
# or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
||||
# in which case the provisions of the GPL or the LGPL are applicable instead
|
||||
# of those above. If you wish to allow use of your version of this file only
|
||||
# under the terms of either the GPL or the LGPL, and not to allow others to
|
||||
# use your version of this file under the terms of the MPL, indicate your
|
||||
# decision by deleting the provisions above and replace them with the notice
|
||||
# and other provisions required by the GPL or the LGPL. If you do not delete
|
||||
# the provisions above, a recipient may use your version of this file under
|
||||
# the terms of any one of the MPL, the GPL or the LGPL.
|
||||
#
|
||||
# ***** END LICENSE BLOCK *****
|
||||
|
||||
DEPTH = ../../..
|
||||
topsrcdir = @top_srcdir@
|
||||
srcdir = @srcdir@
|
||||
VPATH = @srcdir@
|
||||
|
||||
include $(DEPTH)/config/autoconf.mk
|
||||
|
||||
INTERNAL_TOOLS = 1
|
||||
|
||||
HOST_PROGRAM = elfhack
|
||||
NO_DIST_INSTALL = 1
|
||||
|
||||
HOST_CPPSRCS = \
|
||||
elf.cpp \
|
||||
elfhack.cpp \
|
||||
$(NULL)
|
||||
|
||||
OS_CXXFLAGS += -fexceptions
|
||||
|
||||
ifneq (,$(filter %86,$(TARGET_CPU)))
|
||||
CPU := x86
|
||||
else
|
||||
CPU := $(TARGET_CPU)
|
||||
endif
|
||||
|
||||
CSRCS := \
|
||||
inject/$(CPU).c \
|
||||
inject/$(CPU)-noinit.c \
|
||||
$(NULL)
|
||||
|
||||
libs:: $(CSRCS:.c=.$(OBJ_SUFFIX))
|
||||
|
||||
ifndef CROSS_COMPILE
|
||||
test$(DLL_SUFFIX): test.$(OBJ_SUFFIX) elfhack $(CSRCS:.c=.$(OBJ_SUFFIX))
|
||||
$(MKSHLIB) $<
|
||||
rm -f $@.bak
|
||||
$(CURDIR)/elfhack -b $@
|
||||
# Fail if the backup file doesn't exist
|
||||
[ -f "$@.bak" ]
|
||||
# Fail if the new library doesn't contain less relocations
|
||||
[ $$(objdump -R $@.bak | wc -l) -gt $$(objdump -R $@ | wc -l) ]
|
||||
|
||||
dummy: dummy.$(OBJ_SUFFIX) test$(DLL_SUFFIX)
|
||||
$(CC) -o $@ $^
|
||||
|
||||
libs:: dummy
|
||||
# Will either crash or return exit code 1 if elfhack is broken
|
||||
LD_LIBRARY_PATH=$(CURDIR) $(CURDIR)/dummy
|
||||
|
||||
CSRCS += test.c dummy.c
|
||||
|
||||
GARBAGE += dummy test$(DLL_SUFFIX) test$(DLL_SUFFIX).bak
|
||||
endif
|
||||
|
||||
inject:
|
||||
$(NSINSTALL) -D $@
|
||||
|
||||
inject/%.c: inject.c | inject
|
||||
cp $< $@
|
||||
|
||||
GARBAGE_DIRS += inject
|
||||
|
||||
inject/%.$(OBJ_SUFFIX): DEFINES += -DBITS=$(if $(HAVE_64BIT_OS),64,32)
|
||||
inject/$(CPU)-noinit.$(OBJ_SUFFIX): DEFINES += -DNOINIT
|
||||
|
||||
# need this to suppress errors due to /usr/include/linux/byteorder/swab.h
|
||||
# on mozilla buildbots
|
||||
OS_CXXFLAGS := $(filter-out -pedantic,$(OS_CXXFLAGS))
|
||||
|
||||
# Avoid building as thumb code, this is not supported yet
|
||||
COMMA := ,
|
||||
OS_CFLAGS := $(filter-out -mthumb -Wa$(COMMA)-mthumb,$(OS_CFLAGS))
|
||||
|
||||
include $(topsrcdir)/config/rules.mk
|
|
@ -0,0 +1,28 @@
|
|||
Elfhack is a program to optimize ELF binaries for size and cold startup
|
||||
speed.
|
||||
|
||||
Presently, it is quite experimental, though it works well for the target
|
||||
it was created for: Firefox's libxul.so.
|
||||
|
||||
Elfhack currently only does one thing: packing dynamic relocations ;
|
||||
which ends up being a quite complex task, that can be summarized this
|
||||
way:
|
||||
- Remove RELATIVE relocations from the .rel.dyn/.rela.dyn section.
|
||||
- Inject a small code able to apply relative relocations "by hand"
|
||||
after the .rel.dyn/.rela.dyn section.
|
||||
- Inject a section containing relocative relocations in a different
|
||||
and more packed format, after the small code.
|
||||
- Register the small code as DT_INIT function. Make the small code call
|
||||
what was initially the DT_INIT function, if there was one.
|
||||
- Remove the hole between the new section containing relative
|
||||
relocations and the following sections, adjusting offsets and base
|
||||
addresses accordingly.
|
||||
- Adjust PT_LOAD entries to fit new offsets, and add an additional
|
||||
PT_LOAD entry when that is necessary to handle the discrepancy between
|
||||
offsets and base addresses, meaning the section offsets may yet again
|
||||
need adjustments.
|
||||
- Adjust various DT_* dynamic tags to fit the new ELF layout.
|
||||
- Adjust section headers.
|
||||
- Adjust ELF headers.
|
||||
|
||||
See http://glandium.org/blog/?p=1177#relocations for some figures.
|
|
@ -0,0 +1,3 @@
|
|||
int main() {
|
||||
return 1;
|
||||
}
|
|
@ -0,0 +1,822 @@
|
|||
/* ***** BEGIN LICENSE BLOCK *****
|
||||
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
||||
*
|
||||
* The contents of this file are subject to the Mozilla Public License Version
|
||||
* 1.1 (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
* http://www.mozilla.org/MPL/
|
||||
*
|
||||
* Software distributed under the License is distributed on an "AS IS" basis,
|
||||
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
||||
* for the specific language governing rights and limitations under the
|
||||
* License.
|
||||
*
|
||||
* The Original Code is elfhack.
|
||||
*
|
||||
* The Initial Developer of the Original Code is
|
||||
* Mozilla Foundation.
|
||||
* Portions created by the Initial Developer are Copyright (C) 2010
|
||||
* the Initial Developer. All Rights Reserved.
|
||||
*
|
||||
* Contributor(s):
|
||||
* Mike Hommey <mh@glandium.org>
|
||||
*
|
||||
* Alternatively, the contents of this file may be used under the terms of
|
||||
* either the GNU General Public License Version 2 or later (the "GPL"), or
|
||||
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
||||
* in which case the provisions of the GPL or the LGPL are applicable instead
|
||||
* of those above. If you wish to allow use of your version of this file only
|
||||
* under the terms of either the GPL or the LGPL, and not to allow others to
|
||||
* use your version of this file under the terms of the MPL, indicate your
|
||||
* decision by deleting the provisions above and replace them with the notice
|
||||
* and other provisions required by the GPL or the LGPL. If you do not delete
|
||||
* the provisions above, a recipient may use your version of this file under
|
||||
* the terms of any one of the MPL, the GPL or the LGPL.
|
||||
*
|
||||
* ***** END LICENSE BLOCK ***** */
|
||||
|
||||
#include <cstring>
|
||||
#include <assert.h>
|
||||
#include "elfxx.h"
|
||||
|
||||
template <class endian, typename R, typename T>
|
||||
inline void Elf_Ehdr_Traits::swap(T &t, R &r)
|
||||
{
|
||||
memcpy(r.e_ident, t.e_ident, sizeof(r.e_ident));
|
||||
r.e_type = endian::swap(t.e_type);
|
||||
r.e_machine = endian::swap(t.e_machine);
|
||||
r.e_version = endian::swap(t.e_version);
|
||||
r.e_entry = endian::swap(t.e_entry);
|
||||
r.e_phoff = endian::swap(t.e_phoff);
|
||||
r.e_shoff = endian::swap(t.e_shoff);
|
||||
r.e_flags = endian::swap(t.e_flags);
|
||||
r.e_ehsize = endian::swap(t.e_ehsize);
|
||||
r.e_phentsize = endian::swap(t.e_phentsize);
|
||||
r.e_phnum = endian::swap(t.e_phnum);
|
||||
r.e_shentsize = endian::swap(t.e_shentsize);
|
||||
r.e_shnum = endian::swap(t.e_shnum);
|
||||
r.e_shstrndx = endian::swap(t.e_shstrndx);
|
||||
}
|
||||
|
||||
template <class endian, typename R, typename T>
|
||||
inline void Elf_Phdr_Traits::swap(T &t, R &r)
|
||||
{
|
||||
r.p_type = endian::swap(t.p_type);
|
||||
r.p_offset = endian::swap(t.p_offset);
|
||||
r.p_vaddr = endian::swap(t.p_vaddr);
|
||||
r.p_paddr = endian::swap(t.p_paddr);
|
||||
r.p_filesz = endian::swap(t.p_filesz);
|
||||
r.p_memsz = endian::swap(t.p_memsz);
|
||||
r.p_flags = endian::swap(t.p_flags);
|
||||
r.p_align = endian::swap(t.p_align);
|
||||
}
|
||||
|
||||
template <class endian, typename R, typename T>
|
||||
inline void Elf_Shdr_Traits::swap(T &t, R &r)
|
||||
{
|
||||
r.sh_name = endian::swap(t.sh_name);
|
||||
r.sh_type = endian::swap(t.sh_type);
|
||||
r.sh_flags = endian::swap(t.sh_flags);
|
||||
r.sh_addr = endian::swap(t.sh_addr);
|
||||
r.sh_offset = endian::swap(t.sh_offset);
|
||||
r.sh_size = endian::swap(t.sh_size);
|
||||
r.sh_link = endian::swap(t.sh_link);
|
||||
r.sh_info = endian::swap(t.sh_info);
|
||||
r.sh_addralign = endian::swap(t.sh_addralign);
|
||||
r.sh_entsize = endian::swap(t.sh_entsize);
|
||||
}
|
||||
|
||||
template <class endian, typename R, typename T>
|
||||
inline void Elf_Dyn_Traits::swap(T &t, R &r)
|
||||
{
|
||||
r.d_tag = endian::swap(t.d_tag);
|
||||
r.d_un.d_val = endian::swap(t.d_un.d_val);
|
||||
}
|
||||
|
||||
template <class endian, typename R, typename T>
|
||||
inline void Elf_Sym_Traits::swap(T &t, R &r)
|
||||
{
|
||||
r.st_name = endian::swap(t.st_name);
|
||||
r.st_value = endian::swap(t.st_value);
|
||||
r.st_size = endian::swap(t.st_size);
|
||||
r.st_info = t.st_info;
|
||||
r.st_other = t.st_other;
|
||||
r.st_shndx = endian::swap(t.st_shndx);
|
||||
}
|
||||
|
||||
template <class endian>
|
||||
struct _Rel_info {
|
||||
static inline void swap(Elf32_Word &t, Elf32_Word &r) { r = endian::swap(t); }
|
||||
static inline void swap(Elf64_Xword &t, Elf64_Xword &r) { r = endian::swap(t); }
|
||||
static inline void swap(Elf64_Xword &t, Elf32_Word &r) {
|
||||
r = endian::swap(ELF32_R_INFO(ELF64_R_SYM(t), ELF64_R_TYPE(t)));
|
||||
}
|
||||
static inline void swap(Elf32_Word &t, Elf64_Xword &r) {
|
||||
r = endian::swap(ELF64_R_INFO(ELF32_R_SYM(t), ELF32_R_TYPE(t)));
|
||||
}
|
||||
};
|
||||
|
||||
template <class endian, typename R, typename T>
|
||||
inline void Elf_Rel_Traits::swap(T &t, R &r)
|
||||
{
|
||||
r.r_offset = endian::swap(t.r_offset);
|
||||
_Rel_info<endian>::swap(t.r_info, r.r_info);
|
||||
}
|
||||
|
||||
template <class endian, typename R, typename T>
|
||||
inline void Elf_Rela_Traits::swap(T &t, R &r)
|
||||
{
|
||||
r.r_offset = endian::swap(t.r_offset);
|
||||
_Rel_info<endian>::swap(t.r_info, r.r_info);
|
||||
r.r_addend = endian::swap(t.r_addend);
|
||||
}
|
||||
|
||||
static const Elf32_Shdr null32_section =
|
||||
{ 0, SHT_NULL, 0, 0, 0, 0, SHN_UNDEF, 0, 0, 0 };
|
||||
|
||||
Elf_Shdr null_section(null32_section);
|
||||
|
||||
Elf_Ehdr::Elf_Ehdr(std::ifstream &file, char ei_class, char ei_data)
|
||||
: serializable<Elf_Ehdr_Traits>(file, ei_class, ei_data),
|
||||
ElfSection(null_section, NULL, NULL)
|
||||
{
|
||||
shdr.sh_size = Elf_Ehdr::size(ei_class);
|
||||
}
|
||||
|
||||
Elf::Elf(std::ifstream &file)
|
||||
{
|
||||
if (!file.is_open())
|
||||
throw std::runtime_error("Error opening file");
|
||||
|
||||
file.exceptions(std::ifstream::eofbit | std::ifstream::failbit | std::ifstream::badbit);
|
||||
// Read ELF magic number and identification information
|
||||
char e_ident[EI_VERSION];
|
||||
file.seekg(0);
|
||||
file.read(e_ident, sizeof(e_ident));
|
||||
file.seekg(0);
|
||||
ehdr = new Elf_Ehdr(file, e_ident[EI_CLASS], e_ident[EI_DATA]);
|
||||
|
||||
// ELFOSABI_LINUX is kept unsupported because I haven't looked whether
|
||||
// STB_GNU_UNIQUE or STT_GNU_IFUNC would need special casing.
|
||||
if ((ehdr->e_ident[EI_OSABI] != ELFOSABI_NONE) && (ehdr->e_ident[EI_ABIVERSION] != 0))
|
||||
throw std::runtime_error("unsupported ELF ABI");
|
||||
|
||||
if (ehdr->e_version != 1)
|
||||
throw std::runtime_error("unsupported ELF version");
|
||||
|
||||
// Sanity checks
|
||||
if (ehdr->e_shnum == 0)
|
||||
throw std::runtime_error("sstripped ELF files aren't supported");
|
||||
|
||||
if (ehdr->e_ehsize != Elf_Ehdr::size(e_ident[EI_CLASS]))
|
||||
throw std::runtime_error("unsupported ELF inconsistency: ehdr.e_ehsize != sizeof(ehdr)");
|
||||
|
||||
if (ehdr->e_shentsize != Elf_Shdr::size(e_ident[EI_CLASS]))
|
||||
throw std::runtime_error("unsupported ELF inconsistency: ehdr.e_shentsize != sizeof(shdr)");
|
||||
|
||||
if (ehdr->e_phnum == 0) {
|
||||
if (ehdr->e_phoff != 0)
|
||||
throw std::runtime_error("unsupported ELF inconsistency: e_phnum == 0 && e_phoff != 0");
|
||||
if (ehdr->e_phentsize != 0)
|
||||
throw std::runtime_error("unsupported ELF inconsistency: e_phnum == 0 && e_phentsize != 0");
|
||||
} else if (ehdr->e_phoff != ehdr->e_ehsize)
|
||||
throw std::runtime_error("unsupported ELF inconsistency: ehdr->e_phoff != ehdr->e_ehsize");
|
||||
else if (ehdr->e_phentsize != Elf_Phdr::size(e_ident[EI_CLASS]))
|
||||
throw std::runtime_error("unsupported ELF inconsistency: ehdr->e_phentsize != sizeof(phdr)");
|
||||
|
||||
// Read section headers
|
||||
Elf_Shdr **shdr = new Elf_Shdr *[ehdr->e_shnum];
|
||||
file.seekg(ehdr->e_shoff);
|
||||
for (int i = 0; i < ehdr->e_shnum; i++)
|
||||
shdr[i] = new Elf_Shdr(file, e_ident[EI_CLASS], e_ident[EI_DATA]);
|
||||
|
||||
// Sanity check in section header for index 0
|
||||
if ((shdr[0]->sh_name != 0) || (shdr[0]->sh_type != SHT_NULL) ||
|
||||
(shdr[0]->sh_flags != 0) || (shdr[0]->sh_addr != 0) ||
|
||||
(shdr[0]->sh_offset != 0) || (shdr[0]->sh_size != 0) ||
|
||||
(shdr[0]->sh_link != SHN_UNDEF) || (shdr[0]->sh_info != 0) ||
|
||||
(shdr[0]->sh_addralign != 0) || (shdr[0]->sh_entsize != 0))
|
||||
throw std::runtime_error("Section header for index 0 contains unsupported values");
|
||||
|
||||
if ((shdr[ehdr->e_shstrndx]->sh_link != 0) || (shdr[ehdr->e_shstrndx]->sh_info != 0))
|
||||
throw std::runtime_error("unsupported ELF content: string table with sh_link != 0 || sh_info != 0");
|
||||
|
||||
// Store these temporarily
|
||||
tmp_shdr = shdr;
|
||||
tmp_file = &file;
|
||||
|
||||
// Fill sections list
|
||||
sections = new ElfSection *[ehdr->e_shnum];
|
||||
for (int i = 0; i < ehdr->e_shnum; i++)
|
||||
sections[i] = NULL;
|
||||
for (int i = 1; i < ehdr->e_shnum; i++) {
|
||||
if (sections[i] != NULL)
|
||||
continue;
|
||||
getSection(i);
|
||||
}
|
||||
Elf_Shdr s;
|
||||
s.sh_name = 0;
|
||||
s.sh_type = SHT_NULL;
|
||||
s.sh_flags = 0;
|
||||
s.sh_addr = 0;
|
||||
s.sh_offset = ehdr->e_shoff;
|
||||
s.sh_entsize = Elf_Shdr::size(e_ident[EI_CLASS]);
|
||||
s.sh_size = s.sh_entsize * ehdr->e_shnum;
|
||||
s.sh_link = 0;
|
||||
s.sh_info = 0;
|
||||
s.sh_addralign = (e_ident[EI_CLASS] == ELFCLASS32) ? 4 : 8;
|
||||
shdr_section = new ElfSection(s, NULL, NULL);
|
||||
|
||||
// Fake section for program headers
|
||||
s.sh_offset = ehdr->e_phoff;
|
||||
s.sh_entsize = Elf_Phdr::size(e_ident[EI_CLASS]);
|
||||
s.sh_size = s.sh_entsize * ehdr->e_phnum;
|
||||
phdr_section = new ElfSection(s, NULL, NULL);
|
||||
|
||||
phdr_section->insertAfter(ehdr);
|
||||
|
||||
sections[1]->insertAfter(phdr_section);
|
||||
for (int i = 2; i < ehdr->e_shnum; i++) {
|
||||
// TODO: this should be done in a better way
|
||||
if ((shdr_section->getPrevious() == NULL) && (shdr[i]->sh_offset > ehdr->e_shoff)) {
|
||||
shdr_section->insertAfter(sections[i - 1]);
|
||||
sections[i]->insertAfter(shdr_section);
|
||||
} else
|
||||
sections[i]->insertAfter(sections[i - 1]);
|
||||
}
|
||||
if (shdr_section->getPrevious() == NULL)
|
||||
shdr_section->insertAfter(sections[ehdr->e_shnum - 1]);
|
||||
|
||||
tmp_file = NULL;
|
||||
tmp_shdr = NULL;
|
||||
for (int i = 0; i < ehdr->e_shnum; i++)
|
||||
delete shdr[i];
|
||||
delete[] shdr;
|
||||
|
||||
eh_shstrndx = (ElfStrtab_Section *)sections[ehdr->e_shstrndx];
|
||||
|
||||
// Skip reading program headers if there aren't any
|
||||
if (ehdr->e_phnum == 0)
|
||||
return;
|
||||
|
||||
// Read program headers
|
||||
file.seekg(ehdr->e_phoff);
|
||||
for (int i = 0; i < ehdr->e_phnum; i++) {
|
||||
Elf_Phdr phdr(file, e_ident[EI_CLASS], e_ident[EI_DATA]);
|
||||
ElfSegment *segment = new ElfSegment(&phdr);
|
||||
// Some segments aren't entirely filled (if at all) by sections
|
||||
// For those, we use fake sections
|
||||
if ((phdr.p_type == PT_LOAD) && (phdr.p_offset == 0)) {
|
||||
// Use a fake section for ehdr and phdr
|
||||
ehdr->getShdr().sh_addr = phdr.p_vaddr;
|
||||
phdr_section->getShdr().sh_addr = phdr.p_vaddr + ehdr->e_ehsize;
|
||||
segment->addSection(ehdr);
|
||||
segment->addSection(phdr_section);
|
||||
ehdr->markDirty();
|
||||
}
|
||||
if (phdr.p_type == PT_PHDR)
|
||||
segment->addSection(phdr_section);
|
||||
for (int j = 1; j < ehdr->e_shnum; j++)
|
||||
if (phdr.contains(sections[j]))
|
||||
segment->addSection(sections[j]);
|
||||
// Make sure that our view of segments corresponds to the original
|
||||
// ELF file.
|
||||
assert(segment->getFileSize() == phdr.p_filesz);
|
||||
assert(segment->getMemSize() == phdr.p_memsz);
|
||||
segments.push_back(segment);
|
||||
}
|
||||
|
||||
new (&eh_entry) ElfLocation(ehdr->e_entry, this);
|
||||
}
|
||||
|
||||
Elf::~Elf()
|
||||
{
|
||||
for (std::vector<ElfSegment *>::iterator seg = segments.begin(); seg != segments.end(); seg++)
|
||||
delete *seg;
|
||||
delete[] sections;
|
||||
ElfSection *section = ehdr;
|
||||
while (section != NULL) {
|
||||
ElfSection *next = section->getNext();
|
||||
delete section;
|
||||
section = next;
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: This shouldn't fail after inserting sections
|
||||
ElfSection *Elf::getSection(int index)
|
||||
{
|
||||
if ((index < -1) || (index >= ehdr->e_shnum))
|
||||
throw std::runtime_error("Section index out of bounds");
|
||||
if (index == -1)
|
||||
index = ehdr->e_shstrndx; // TODO: should be fixed to use the actual current number
|
||||
// Special case: the section at index 0 is void
|
||||
if (index == 0)
|
||||
return NULL;
|
||||
// Infinite recursion guard
|
||||
if (sections[index] == (ElfSection *)this)
|
||||
return NULL;
|
||||
if (sections[index] == NULL) {
|
||||
sections[index] = (ElfSection *)this;
|
||||
switch (tmp_shdr[index]->sh_type) {
|
||||
case SHT_DYNAMIC:
|
||||
sections[index] = new ElfDynamic_Section(*tmp_shdr[index], tmp_file, this);
|
||||
break;
|
||||
case SHT_REL:
|
||||
sections[index] = new ElfRel_Section<Elf_Rel>(*tmp_shdr[index], tmp_file, this);
|
||||
break;
|
||||
case SHT_RELA:
|
||||
sections[index] = new ElfRel_Section<Elf_Rela>(*tmp_shdr[index], tmp_file, this);
|
||||
break;
|
||||
case SHT_SYMTAB:
|
||||
sections[index] = new ElfSymtab_Section(*tmp_shdr[index], tmp_file, this);
|
||||
break;
|
||||
case SHT_STRTAB:
|
||||
sections[index] = new ElfStrtab_Section(*tmp_shdr[index], tmp_file, this);
|
||||
break;
|
||||
default:
|
||||
sections[index] = new ElfSection(*tmp_shdr[index], tmp_file, this);
|
||||
}
|
||||
}
|
||||
return sections[index];
|
||||
}
|
||||
|
||||
ElfSection *Elf::getSectionAt(unsigned int offset)
|
||||
{
|
||||
for (int i = 1; i < ehdr->e_shnum; i++) {
|
||||
ElfSection *section = getSection(i);
|
||||
if ((section != NULL) && (section->getFlags() & SHF_ALLOC) && !(section->getFlags() & SHF_TLS) &&
|
||||
(offset >= section->getAddr()) && (offset < section->getAddr() + section->getSize()))
|
||||
return section;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
ElfDynamic_Section *Elf::getDynSection()
|
||||
{
|
||||
for (std::vector<ElfSegment *>::iterator seg = segments.begin(); seg != segments.end(); seg++)
|
||||
if (((*seg)->getType() == PT_DYNAMIC) && ((*seg)->getFirstSection() != NULL) &&
|
||||
(*seg)->getFirstSection()->getType() == SHT_DYNAMIC)
|
||||
return (ElfDynamic_Section *)(*seg)->getFirstSection();
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void Elf::write(std::ofstream &file)
|
||||
{
|
||||
// fixup section headers sh_name; TODO: that should be done by sections
|
||||
// themselves
|
||||
for (ElfSection *section = ehdr; section != NULL; section = section->getNext()) {
|
||||
if (section->getIndex() == 0)
|
||||
continue;
|
||||
else
|
||||
ehdr->e_shnum = section->getIndex() + 1;
|
||||
section->getShdr().sh_name = eh_shstrndx->getStrIndex(section->getName());
|
||||
}
|
||||
phdr_section->getNext()->markDirty();
|
||||
// Adjust PT_LOAD segments
|
||||
int i = 0;
|
||||
for (std::vector<ElfSegment *>::iterator seg = segments.begin(); seg != segments.end(); seg++, i++) {
|
||||
if ((*seg)->getType() == PT_LOAD) {
|
||||
std::list<ElfSection *>::iterator it = (*seg)->begin();
|
||||
for (ElfSection *last = *(it++); it != (*seg)->end(); last = *(it++)) {
|
||||
if (((*it)->getType() != SHT_NOBITS) &&
|
||||
((*it)->getAddr() - last->getAddr()) != ((*it)->getOffset() - last->getOffset())) {
|
||||
std::vector<ElfSegment *>::iterator next = seg;
|
||||
segments.insert(++next, (*seg)->splitBefore(*it));
|
||||
seg = segments.begin() + i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// fixup ehdr before writing
|
||||
if (ehdr->e_phnum != segments.size()) {
|
||||
ehdr->e_phnum = segments.size();
|
||||
phdr_section->getShdr().sh_size = segments.size() * Elf_Phdr::size(ehdr->e_ident[EI_CLASS]);
|
||||
phdr_section->getNext()->markDirty();
|
||||
}
|
||||
// fixup shdr before writing
|
||||
if (ehdr->e_shnum != shdr_section->getSize() / shdr_section->getEntSize())
|
||||
shdr_section->getShdr().sh_size = ehdr->e_shnum * Elf_Shdr::size(ehdr->e_ident[EI_CLASS]);
|
||||
ehdr->e_shoff = shdr_section->getOffset();
|
||||
ehdr->e_entry = eh_entry.getValue();
|
||||
ehdr->e_shstrndx = eh_shstrndx->getIndex();
|
||||
for (ElfSection *section = ehdr;
|
||||
section != NULL; section = section->getNext()) {
|
||||
file.seekp(section->getOffset());
|
||||
if (section == phdr_section) {
|
||||
for (std::vector<ElfSegment *>::iterator seg = segments.begin(); seg != segments.end(); seg++) {
|
||||
Elf_Phdr phdr;
|
||||
phdr.p_type = (*seg)->getType();
|
||||
phdr.p_flags = (*seg)->getFlags();
|
||||
if ((*seg)->getFirstSection()) {
|
||||
phdr.p_offset = (*seg)->getFirstSection()->getOffset();
|
||||
phdr.p_vaddr = (*seg)->getFirstSection()->getAddr();
|
||||
} else {
|
||||
phdr.p_offset = 0;
|
||||
phdr.p_vaddr = 0;
|
||||
}
|
||||
phdr.p_paddr = phdr.p_vaddr + (*seg)->getVPDiff();
|
||||
phdr.p_filesz = (*seg)->getFileSize();
|
||||
phdr.p_memsz = (*seg)->getMemSize();
|
||||
phdr.p_align = (*seg)->getAlign();
|
||||
phdr.serialize(file, ehdr->e_ident[EI_CLASS], ehdr->e_ident[EI_DATA]);
|
||||
}
|
||||
} else if (section == shdr_section) {
|
||||
null_section.serialize(file, ehdr->e_ident[EI_CLASS], ehdr->e_ident[EI_DATA]);
|
||||
for (ElfSection *sec = ehdr; sec!= NULL; sec = sec->getNext()) {
|
||||
if (sec->getType() != SHT_NULL)
|
||||
sec->getShdr().serialize(file, ehdr->e_ident[EI_CLASS], ehdr->e_ident[EI_DATA]);
|
||||
}
|
||||
} else
|
||||
section->serialize(file, ehdr->e_ident[EI_CLASS], ehdr->e_ident[EI_DATA]);
|
||||
}
|
||||
}
|
||||
|
||||
ElfSection::ElfSection(Elf_Shdr &s, std::ifstream *file, Elf *parent)
|
||||
: shdr(s),
|
||||
link(shdr.sh_link == SHN_UNDEF ? NULL : parent->getSection(shdr.sh_link)),
|
||||
next(NULL), previous(NULL), index(-1)
|
||||
{
|
||||
if ((file == NULL) || (shdr.sh_type == SHT_NULL) || (shdr.sh_type == SHT_NOBITS))
|
||||
data = NULL;
|
||||
else {
|
||||
data = new char[shdr.sh_size];
|
||||
int pos = file->tellg();
|
||||
file->seekg(shdr.sh_offset);
|
||||
file->read(data, shdr.sh_size);
|
||||
file->seekg(pos);
|
||||
}
|
||||
if (shdr.sh_name == 0)
|
||||
name = NULL;
|
||||
else {
|
||||
ElfStrtab_Section *strtab = (ElfStrtab_Section *) parent->getSection(-1);
|
||||
// Special case (see elfgeneric.cpp): if strtab is NULL, the
|
||||
// section being created is the strtab.
|
||||
if (strtab == NULL)
|
||||
name = &data[shdr.sh_name];
|
||||
else
|
||||
name = strtab->getStr(shdr.sh_name);
|
||||
}
|
||||
// Only SHT_REL/SHT_RELA sections use sh_info to store a section
|
||||
// number.
|
||||
if ((shdr.sh_type == SHT_REL) || (shdr.sh_type == SHT_RELA))
|
||||
info.section = shdr.sh_info ? parent->getSection(shdr.sh_info) : NULL;
|
||||
else
|
||||
info.index = shdr.sh_info;
|
||||
}
|
||||
|
||||
unsigned int ElfSection::getAddr()
|
||||
{
|
||||
if (shdr.sh_addr != (Elf32_Word)-1)
|
||||
return shdr.sh_addr;
|
||||
|
||||
// It should be safe to adjust sh_addr for all allocated sections that
|
||||
// are neither SHT_NOBITS nor SHT_PROGBITS
|
||||
if ((previous != NULL) && isRelocatable()) {
|
||||
unsigned int addr = previous->getAddr();
|
||||
if (previous->getType() != SHT_NOBITS)
|
||||
addr += previous->getSize();
|
||||
|
||||
if (addr & (getAddrAlign() - 1))
|
||||
addr = (addr | (getAddrAlign() - 1)) + 1;
|
||||
|
||||
return (shdr.sh_addr = addr);
|
||||
}
|
||||
return shdr.sh_addr;
|
||||
}
|
||||
|
||||
unsigned int ElfSection::getOffset()
|
||||
{
|
||||
if (shdr.sh_offset != (Elf32_Word)-1)
|
||||
return shdr.sh_offset;
|
||||
|
||||
if (previous == NULL)
|
||||
return (shdr.sh_offset = 0);
|
||||
|
||||
unsigned int offset = previous->getOffset();
|
||||
if (previous->getType() != SHT_NOBITS)
|
||||
offset += previous->getSize();
|
||||
|
||||
// SHF_TLS is used for .tbss which is some kind of special case.
|
||||
if (((getType() != SHT_NOBITS) || (getFlags() & SHF_TLS)) && (getFlags() & SHF_ALLOC)) {
|
||||
if ((getAddr() & 4095) < (offset & 4095))
|
||||
offset = (offset | 4095) + (getAddr() & 4095) + 1;
|
||||
else
|
||||
offset = (offset & ~4095) + (getAddr() & 4095);
|
||||
}
|
||||
// TODO: carefully handle this, as this isn't always safe, cf. when
|
||||
// resizing .dynamic.
|
||||
if ((getType() != SHT_NOBITS) && (offset & (getAddrAlign() - 1)))
|
||||
offset = (offset | (getAddrAlign() - 1)) + 1;
|
||||
|
||||
return (shdr.sh_offset = offset);
|
||||
}
|
||||
|
||||
int ElfSection::getIndex()
|
||||
{
|
||||
if (index != -1)
|
||||
return index;
|
||||
if (getType() == SHT_NULL)
|
||||
return (index = 0);
|
||||
ElfSection *reference;
|
||||
for (reference = previous; (reference != NULL) && (reference->getType() == SHT_NULL); reference = reference->getPrevious());
|
||||
if (reference == NULL)
|
||||
return (index = 1);
|
||||
return (index = reference->getIndex() + 1);
|
||||
}
|
||||
|
||||
Elf_Shdr &ElfSection::getShdr()
|
||||
{
|
||||
getOffset();
|
||||
if (shdr.sh_link == (Elf32_Word)-1)
|
||||
shdr.sh_link = getLink() ? getLink()->getIndex() : 0;
|
||||
if (shdr.sh_info == (Elf32_Word)-1)
|
||||
shdr.sh_info = ((getType() == SHT_REL) || (getType() == SHT_RELA)) ?
|
||||
(getInfo().section ? getInfo().section->getIndex() : 0) :
|
||||
getInfo().index;
|
||||
|
||||
return shdr;
|
||||
}
|
||||
|
||||
ElfSegment::ElfSegment(Elf_Phdr *phdr)
|
||||
: type(phdr->p_type), v_p_diff(phdr->p_paddr - phdr->p_vaddr),
|
||||
flags(phdr->p_flags), align(phdr->p_align) {}
|
||||
|
||||
void ElfSegment::addSection(ElfSection *section)
|
||||
{
|
||||
//TODO: Check overlapping sections
|
||||
std::list<ElfSection *>::iterator i;
|
||||
for (i = sections.begin(); i != sections.end(); ++i)
|
||||
if ((*i)->getAddr() > section->getAddr())
|
||||
break;
|
||||
sections.insert(i, section);
|
||||
}
|
||||
|
||||
unsigned int ElfSegment::getFileSize()
|
||||
{
|
||||
if (sections.empty())
|
||||
return 0;
|
||||
// Search the last section that is not SHT_NOBITS
|
||||
std::list<ElfSection *>::reverse_iterator i;
|
||||
for (i = sections.rbegin(); (i != sections.rend()) && ((*i)->getType() == SHT_NOBITS); ++i);
|
||||
// All sections are SHT_NOBITS
|
||||
if (i == sections.rend())
|
||||
return 0;
|
||||
return ((*i)->getAddr() - sections.front()->getAddr()) + (*i)->getSize();
|
||||
}
|
||||
|
||||
unsigned int ElfSegment::getMemSize()
|
||||
{
|
||||
if (sections.empty())
|
||||
return 0;
|
||||
return (sections.back()->getAddr() - sections.front()->getAddr()) +
|
||||
(sections.back()->getSize());
|
||||
}
|
||||
|
||||
ElfSegment *ElfSegment::splitBefore(ElfSection *section)
|
||||
{
|
||||
std::list<ElfSection *>::iterator i, rm;
|
||||
for (i = sections.begin(); (*i != section) && (i != sections.end()); ++i);
|
||||
if (i == sections.end())
|
||||
return NULL;
|
||||
|
||||
// Probably very wrong.
|
||||
Elf_Phdr phdr;
|
||||
phdr.p_type = type;
|
||||
phdr.p_vaddr = 0;
|
||||
phdr.p_paddr = phdr.p_vaddr + v_p_diff;
|
||||
phdr.p_flags = flags;
|
||||
phdr.p_align = 0x1000;
|
||||
ElfSegment *segment = new ElfSegment(&phdr);
|
||||
|
||||
for (rm = i; i != sections.end(); ++i)
|
||||
segment->addSection(*i);
|
||||
sections.erase(rm, sections.end());
|
||||
|
||||
return segment;
|
||||
}
|
||||
|
||||
ElfSection *ElfDynamic_Section::getSectionForType(unsigned int tag)
|
||||
{
|
||||
for (unsigned int i = 0; i < shdr.sh_size / shdr.sh_entsize; i++)
|
||||
if (dyns[i].tag == tag)
|
||||
return dyns[i].value->getSection();
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void ElfDynamic_Section::setValueForType(unsigned int tag, ElfValue *val)
|
||||
{
|
||||
unsigned int i;
|
||||
for (i = 0; (i < shdr.sh_size / shdr.sh_entsize) && (dyns[i].tag != DT_NULL); i++)
|
||||
if (dyns[i].tag == tag) {
|
||||
delete dyns[i].value;
|
||||
dyns[i].value = val;
|
||||
return;
|
||||
}
|
||||
// This should never happen, as the last entry is always tagged DT_NULL
|
||||
assert(i < shdr.sh_size / shdr.sh_entsize);
|
||||
// If we get here, this means we didn't match for the given tag
|
||||
dyns[i].tag = tag;
|
||||
dyns[i++].value = val;
|
||||
|
||||
// If we were on the last entry, we need to grow the section.
|
||||
// Most of the time, though, there are a few DT_NULL entries.
|
||||
if (i < shdr.sh_size / shdr.sh_entsize)
|
||||
return;
|
||||
|
||||
Elf_DynValue value;
|
||||
value.tag = DT_NULL;
|
||||
value.value = NULL;
|
||||
dyns.push_back(value);
|
||||
// Resize the section accordingly
|
||||
shdr.sh_size += shdr.sh_entsize;
|
||||
if (getNext() != NULL)
|
||||
getNext()->markDirty();
|
||||
}
|
||||
|
||||
ElfDynamic_Section::ElfDynamic_Section(Elf_Shdr &s, std::ifstream *file, Elf *parent)
|
||||
: ElfSection(s, file, parent)
|
||||
{
|
||||
int pos = file->tellg();
|
||||
dyns.resize(s.sh_size / s.sh_entsize);
|
||||
file->seekg(shdr.sh_offset);
|
||||
// Here we assume tags refer to only one section (e.g. DT_RELSZ accounts
|
||||
// for .rel.dyn size)
|
||||
for (unsigned int i = 0; i < s.sh_size / s.sh_entsize; i++) {
|
||||
Elf_Dyn dyn(*file, parent->getClass(), parent->getData());
|
||||
dyns[i].tag = dyn.d_tag;
|
||||
switch (dyn.d_tag) {
|
||||
case DT_NULL:
|
||||
case DT_SYMBOLIC:
|
||||
case DT_TEXTREL:
|
||||
case DT_BIND_NOW:
|
||||
dyns[i].value = new ElfValue();
|
||||
break;
|
||||
case DT_NEEDED:
|
||||
case DT_SONAME:
|
||||
case DT_RPATH:
|
||||
case DT_PLTREL:
|
||||
case DT_RUNPATH:
|
||||
case DT_FLAGS:
|
||||
case DT_RELACOUNT:
|
||||
case DT_RELCOUNT:
|
||||
case DT_VERDEFNUM:
|
||||
case DT_VERNEEDNUM:
|
||||
dyns[i].value = new ElfPlainValue(dyn.d_un.d_val);
|
||||
break;
|
||||
case DT_PLTGOT:
|
||||
case DT_HASH:
|
||||
case DT_STRTAB:
|
||||
case DT_SYMTAB:
|
||||
case DT_RELA:
|
||||
case DT_INIT:
|
||||
case DT_FINI:
|
||||
case DT_REL:
|
||||
case DT_JMPREL:
|
||||
case DT_INIT_ARRAY:
|
||||
case DT_FINI_ARRAY:
|
||||
case DT_GNU_HASH:
|
||||
case DT_VERSYM:
|
||||
case DT_VERNEED:
|
||||
case DT_VERDEF:
|
||||
dyns[i].value = new ElfLocation(dyn.d_un.d_ptr, parent);
|
||||
break;
|
||||
default:
|
||||
dyns[i].value = NULL;
|
||||
}
|
||||
}
|
||||
// Another loop to get the section sizes
|
||||
for (unsigned int i = 0; i < s.sh_size / s.sh_entsize; i++)
|
||||
switch (dyns[i].tag) {
|
||||
case DT_PLTRELSZ:
|
||||
dyns[i].value = new ElfSize(getSectionForType(DT_JMPREL));
|
||||
break;
|
||||
case DT_RELASZ:
|
||||
dyns[i].value = new ElfSize(getSectionForType(DT_RELA));
|
||||
break;
|
||||
case DT_STRSZ:
|
||||
dyns[i].value = new ElfSize(getSectionForType(DT_STRTAB));
|
||||
break;
|
||||
case DT_RELSZ:
|
||||
dyns[i].value = new ElfSize(getSectionForType(DT_REL));
|
||||
break;
|
||||
case DT_INIT_ARRAYSZ:
|
||||
dyns[i].value = new ElfSize(getSectionForType(DT_INIT_ARRAY));
|
||||
break;
|
||||
case DT_FINI_ARRAYSZ:
|
||||
dyns[i].value = new ElfSize(getSectionForType(DT_FINI_ARRAY));
|
||||
break;
|
||||
case DT_RELAENT:
|
||||
dyns[i].value = new ElfEntSize(getSectionForType(DT_RELA));
|
||||
break;
|
||||
case DT_SYMENT:
|
||||
dyns[i].value = new ElfEntSize(getSectionForType(DT_SYMTAB));
|
||||
break;
|
||||
case DT_RELENT:
|
||||
dyns[i].value = new ElfEntSize(getSectionForType(DT_REL));
|
||||
break;
|
||||
}
|
||||
|
||||
file->seekg(pos);
|
||||
}
|
||||
|
||||
ElfDynamic_Section::~ElfDynamic_Section()
|
||||
{
|
||||
for (unsigned int i = 0; i < shdr.sh_size / shdr.sh_entsize; i++)
|
||||
delete dyns[i].value;
|
||||
}
|
||||
|
||||
void ElfDynamic_Section::serialize(std::ofstream &file, char ei_class, char ei_data)
|
||||
{
|
||||
for (unsigned int i = 0; i < shdr.sh_size / shdr.sh_entsize; i++) {
|
||||
Elf_Dyn dyn;
|
||||
dyn.d_tag = dyns[i].tag;
|
||||
dyn.d_un.d_val = (dyns[i].value != NULL) ? dyns[i].value->getValue() : 0;
|
||||
dyn.serialize(file, ei_class, ei_data);
|
||||
}
|
||||
}
|
||||
|
||||
ElfSymtab_Section::ElfSymtab_Section(Elf_Shdr &s, std::ifstream *file, Elf *parent)
|
||||
: ElfSection(s, file, parent)
|
||||
{
|
||||
int pos = file->tellg();
|
||||
file->seekg(shdr.sh_offset);
|
||||
for (unsigned int i = 0; i < shdr.sh_size / shdr.sh_entsize; i++) {
|
||||
Elf_Sym sym(*file, parent->getClass(), parent->getData());
|
||||
syms.push_back(sym);
|
||||
}
|
||||
file->seekg(pos);
|
||||
}
|
||||
|
||||
const char *
|
||||
ElfStrtab_Section::getStr(unsigned int index)
|
||||
{
|
||||
for (std::vector<table_storage>::iterator t = table.begin();
|
||||
t != table.end(); t++) {
|
||||
if (index < t->used)
|
||||
return t->buf + index;
|
||||
index -= t->used;
|
||||
}
|
||||
assert(1 == 0);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
const char *
|
||||
ElfStrtab_Section::getStr(const char *string)
|
||||
{
|
||||
if (string == NULL)
|
||||
return NULL;
|
||||
|
||||
// If the given string is within the section, return it
|
||||
for (std::vector<table_storage>::iterator t = table.begin();
|
||||
t != table.end(); t++)
|
||||
if ((string >= t->buf) && (string < t->buf + t->used))
|
||||
return string;
|
||||
|
||||
// TODO: should scan in the section to find an existing string
|
||||
|
||||
// If not, we need to allocate the string in the section
|
||||
size_t len = strlen(string) + 1;
|
||||
|
||||
if (table.back().size - table.back().used < len)
|
||||
table.resize(table.size() + 1);
|
||||
|
||||
char *alloc_str = table.back().buf + table.back().used;
|
||||
memcpy(alloc_str, string, len);
|
||||
table.back().used += len;
|
||||
|
||||
shdr.sh_size += len;
|
||||
markDirty();
|
||||
|
||||
return alloc_str;
|
||||
}
|
||||
|
||||
unsigned int
|
||||
ElfStrtab_Section::getStrIndex(const char *string)
|
||||
{
|
||||
if (string == NULL)
|
||||
return 0;
|
||||
|
||||
unsigned int index = 0;
|
||||
string = getStr(string);
|
||||
for (std::vector<table_storage>::iterator t = table.begin();
|
||||
t != table.end(); t++) {
|
||||
if ((string >= t->buf) && (string < t->buf + t->used))
|
||||
return index + (string - t->buf);
|
||||
index += t->used;
|
||||
}
|
||||
|
||||
assert(1 == 0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void
|
||||
ElfStrtab_Section::serialize(std::ofstream &file, char ei_class, char ei_data)
|
||||
{
|
||||
file.seekp(getOffset());
|
||||
for (std::vector<table_storage>::iterator t = table.begin();
|
||||
t != table.end(); t++)
|
||||
file.write(t->buf, t->used);
|
||||
}
|
|
@ -0,0 +1,414 @@
|
|||
/* ***** BEGIN LICENSE BLOCK *****
|
||||
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
||||
*
|
||||
* The contents of this file are subject to the Mozilla Public License Version
|
||||
* 1.1 (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
* http://www.mozilla.org/MPL/
|
||||
*
|
||||
* Software distributed under the License is distributed on an "AS IS" basis,
|
||||
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
||||
* for the specific language governing rights and limitations under the
|
||||
* License.
|
||||
*
|
||||
* The Original Code is elfhack.
|
||||
*
|
||||
* The Initial Developer of the Original Code is
|
||||
* Mozilla Foundation.
|
||||
* Portions created by the Initial Developer are Copyright (C) 2010
|
||||
* the Initial Developer. All Rights Reserved.
|
||||
*
|
||||
* Contributor(s):
|
||||
* Mike Hommey <mh@glandium.org>
|
||||
*
|
||||
* Alternatively, the contents of this file may be used under the terms of
|
||||
* either the GNU General Public License Version 2 or later (the "GPL"), or
|
||||
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
||||
* in which case the provisions of the GPL or the LGPL are applicable instead
|
||||
* of those above. If you wish to allow use of your version of this file only
|
||||
* under the terms of either the GPL or the LGPL, and not to allow others to
|
||||
* use your version of this file under the terms of the MPL, indicate your
|
||||
* decision by deleting the provisions above and replace them with the notice
|
||||
* and other provisions required by the GPL or the LGPL. If you do not delete
|
||||
* the provisions above, a recipient may use your version of this file under
|
||||
* the terms of any one of the MPL, the GPL or the LGPL.
|
||||
*
|
||||
* ***** END LICENSE BLOCK ***** */
|
||||
|
||||
#include <assert.h>
|
||||
#include <cstring>
|
||||
#include <cstdlib>
|
||||
#include <cstdio>
|
||||
#include "elfxx.h"
|
||||
|
||||
#define ver "0"
|
||||
#define elfhack_data ".elfhack.data.v" ver
|
||||
#define elfhack_text ".elfhack.text.v" ver
|
||||
|
||||
#ifndef R_ARM_V4BX
|
||||
#define R_ARM_V4BX 0x28
|
||||
#endif
|
||||
|
||||
char *rundir = NULL;
|
||||
|
||||
class Elf_RelHack_Traits {
|
||||
public:
|
||||
typedef Elf32_Rel Type32;
|
||||
typedef Elf32_Rel Type64;
|
||||
|
||||
template <class endian, typename R, typename T>
|
||||
static inline void swap(T &t, R &r) {
|
||||
r.r_offset = endian::swap(t.r_offset);
|
||||
r.r_info = endian::swap(t.r_info);
|
||||
}
|
||||
};
|
||||
|
||||
typedef serializable<Elf_RelHack_Traits> Elf_RelHack;
|
||||
|
||||
class ElfRelHack_Section: public ElfSection {
|
||||
public:
|
||||
ElfRelHack_Section(Elf_Shdr &s)
|
||||
: ElfSection(s, NULL, NULL)
|
||||
{
|
||||
name = elfhack_data;
|
||||
};
|
||||
|
||||
void serialize(std::ofstream &file, char ei_class, char ei_data)
|
||||
{
|
||||
for (std::vector<Elf_RelHack>::iterator i = rels.begin();
|
||||
i != rels.end(); ++i)
|
||||
(*i).serialize(file, ei_class, ei_data);
|
||||
}
|
||||
|
||||
bool isRelocatable() {
|
||||
return true;
|
||||
}
|
||||
|
||||
void push_back(Elf_RelHack &r) {
|
||||
rels.push_back(r);
|
||||
shdr.sh_size = rels.size() * shdr.sh_entsize;
|
||||
}
|
||||
private:
|
||||
std::vector<Elf_RelHack> rels;
|
||||
};
|
||||
|
||||
class ElfRelHackCode_Section: public ElfSection {
|
||||
public:
|
||||
ElfRelHackCode_Section(Elf_Shdr &s, Elf &e)
|
||||
: ElfSection(s, NULL, NULL), parent(e) {
|
||||
std::string file(rundir);
|
||||
init = parent.getDynSection()->getSectionForType(DT_INIT);
|
||||
file += "/inject/";
|
||||
switch (parent.getMachine()) {
|
||||
case EM_386:
|
||||
file += "x86";
|
||||
break;
|
||||
case EM_X86_64:
|
||||
file += "x86_64";
|
||||
break;
|
||||
case EM_ARM:
|
||||
file += "arm";
|
||||
break;
|
||||
default:
|
||||
throw std::runtime_error("unsupported architecture");
|
||||
}
|
||||
if (init == NULL)
|
||||
file += "-noinit";
|
||||
file += ".o";
|
||||
std::ifstream inject(file.c_str(), std::ios::in|std::ios::binary);
|
||||
elf = new Elf(inject);
|
||||
if (elf->getType() != ET_REL)
|
||||
throw std::runtime_error("object for injected code is not ET_REL");
|
||||
if (elf->getMachine() != parent.getMachine())
|
||||
throw std::runtime_error("architecture of object for injected code doesn't match");
|
||||
|
||||
// Get all executable sections from the injected code object.
|
||||
// Most of the time, there will only be one for the init function,
|
||||
// but on e.g. x86, there is a separate section for
|
||||
// __i686.get_pc_thunk.$reg
|
||||
for (ElfSection *text = elf->getSection(1); text != NULL;
|
||||
text = text->getNext()) {
|
||||
if ((text->getType() == SHT_PROGBITS) &&
|
||||
(text->getFlags() & SHF_EXECINSTR)) {
|
||||
code.push_back(text);
|
||||
// We need to align this section depending on the greater
|
||||
// alignment required by code sections.
|
||||
if (shdr.sh_addralign < text->getAddrAlign())
|
||||
shdr.sh_addralign = text->getAddrAlign();
|
||||
}
|
||||
}
|
||||
assert(code.size() != 0);
|
||||
|
||||
// Adjust code sections offsets according to their size
|
||||
std::vector<ElfSection *>::iterator c = code.begin();
|
||||
(*c)->getShdr().sh_addr = 0;
|
||||
for(ElfSection *last = *(c++); c != code.end(); c++) {
|
||||
unsigned int addr = last->getShdr().sh_addr + last->getSize();
|
||||
if (addr & ((*c)->getAddrAlign() - 1))
|
||||
addr = (addr | ((*c)->getAddrAlign() - 1)) + 1;
|
||||
(*c)->getShdr().sh_addr = addr;
|
||||
}
|
||||
shdr.sh_size = code.back()->getAddr() + code.back()->getSize();
|
||||
data = new char[shdr.sh_size];
|
||||
char *buf = data;
|
||||
for (c = code.begin(); c != code.end(); c++) {
|
||||
memcpy(buf, (*c)->getData(), (*c)->getSize());
|
||||
buf += (*c)->getSize();
|
||||
}
|
||||
name = elfhack_text;
|
||||
}
|
||||
|
||||
~ElfRelHackCode_Section() {
|
||||
delete elf;
|
||||
}
|
||||
|
||||
void serialize(std::ofstream &file, char ei_class, char ei_data)
|
||||
{
|
||||
// Readjust code offsets
|
||||
for (std::vector<ElfSection *>::iterator c = code.begin(); c != code.end(); c++)
|
||||
(*c)->getShdr().sh_addr += getAddr();
|
||||
|
||||
// Apply relocations
|
||||
for (ElfSection *rel = elf->getSection(1); rel != NULL; rel = rel->getNext())
|
||||
if ((rel->getType() == SHT_REL) || (rel->getType() == SHT_RELA)) {
|
||||
ElfSection *section = rel->getInfo().section;
|
||||
if ((section->getType() == SHT_PROGBITS) && (section->getFlags() & SHF_EXECINSTR)) {
|
||||
if (rel->getType() == SHT_REL)
|
||||
apply_relocations((ElfRel_Section<Elf_Rel> *)rel, section);
|
||||
else
|
||||
apply_relocations((ElfRel_Section<Elf_Rela> *)rel, section);
|
||||
}
|
||||
}
|
||||
|
||||
ElfSection::serialize(file, ei_class, ei_data);
|
||||
}
|
||||
|
||||
bool isRelocatable() {
|
||||
return true;
|
||||
}
|
||||
|
||||
private:
|
||||
void apply_pc32_relocation(ElfSection *the_code, char *base, Elf_Rel *r, unsigned int addr)
|
||||
{
|
||||
*(Elf32_Addr *)(base + r->r_offset) += (Elf32_Addr) (addr - r->r_offset - the_code->getAddr());
|
||||
}
|
||||
|
||||
void apply_pc32_relocation(ElfSection *the_code, char *base, Elf_Rela *r, unsigned int addr)
|
||||
{
|
||||
*(Elf32_Addr *)(base + r->r_offset) = (Elf32_Addr) (addr + r->r_addend - r->r_offset - the_code->getAddr());
|
||||
}
|
||||
|
||||
void apply_arm_plt32_relocation(ElfSection *the_code, char *base, Elf_Rel *r, unsigned int addr)
|
||||
{
|
||||
// We don't care about sign_extend because the only case where this is
|
||||
// going to be used only jumps forward.
|
||||
Elf32_Addr addend = (Elf32_Addr) (addr - r->r_offset - the_code->getAddr()) >> 2;
|
||||
addend = (*(Elf32_Addr *)(base + r->r_offset) + addend) & 0x00ffffff;
|
||||
*(Elf32_Addr *)(base + r->r_offset) = (*(Elf32_Addr *)(base + r->r_offset) & 0xff000000) | addend;
|
||||
}
|
||||
|
||||
void apply_arm_plt32_relocation(ElfSection *the_code, char *base, Elf_Rela *r, unsigned int addr)
|
||||
{
|
||||
// We don't care about sign_extend because the only case where this is
|
||||
// going to be used only jumps forward.
|
||||
Elf32_Addr addend = (Elf32_Addr) (addr - r->r_offset - the_code->getAddr()) >> 2;
|
||||
addend = (r->r_addend + addend) & 0x00ffffff;
|
||||
*(Elf32_Addr *)(base + r->r_offset) = (r->r_addend & 0xff000000) | addend;
|
||||
}
|
||||
|
||||
void apply_gotoff_relocation(ElfSection *the_code, char *base, Elf_Rel *r, unsigned int addr)
|
||||
{
|
||||
*(Elf32_Addr *)(base + r->r_offset) += (Elf32_Addr) addr;
|
||||
}
|
||||
|
||||
void apply_gotoff_relocation(ElfSection *the_code, char *base, Elf_Rela *r, unsigned int addr)
|
||||
{
|
||||
*(Elf32_Addr *)(base + r->r_offset) = (Elf32_Addr) addr + r->r_addend;
|
||||
}
|
||||
|
||||
template <typename Rel_Type>
|
||||
void apply_relocations(ElfRel_Section<Rel_Type> *rel, ElfSection *the_code)
|
||||
{
|
||||
assert(rel->getType() == Rel_Type::sh_type);
|
||||
char *buf = data + (the_code->getAddr() - code.front()->getAddr());
|
||||
// TODO: various checks on the sections
|
||||
ElfSymtab_Section *symtab = (ElfSymtab_Section *)rel->getLink();
|
||||
ElfStrtab_Section *strtab = (ElfStrtab_Section *)symtab->getLink();
|
||||
for (typename std::vector<Rel_Type>::iterator r = rel->rels.begin(); r != rel->rels.end(); r++) {
|
||||
// TODO: various checks on the symbol
|
||||
const char *name = strtab->getStr(symtab->syms[ELF32_R_SYM(r->r_info)].st_name);
|
||||
unsigned int addr;
|
||||
if (symtab->syms[ELF32_R_SYM(r->r_info)].st_shndx == 0) {
|
||||
if (strcmp(name, "relhack") == 0) {
|
||||
addr = getNext()->getAddr();
|
||||
} else if (strcmp(name, "elf_header") == 0) {
|
||||
// TODO: change this ungly hack to something better
|
||||
ElfSection *ehdr = parent.getSection(1)->getPrevious()->getPrevious();
|
||||
addr = ehdr->getAddr();
|
||||
} else if (strcmp(name, "original_init") == 0) {
|
||||
addr = init->getAddr();
|
||||
} else if (strcmp(name, "_GLOBAL_OFFSET_TABLE_") == 0) {
|
||||
// We actually don't need a GOT, but need it as a reference for
|
||||
// GOTOFF relocations. We'll just use the start of the ELF file
|
||||
addr = 0;
|
||||
} else if (strcmp(name, "") == 0) {
|
||||
// This is for R_ARM_V4BX, until we find something better
|
||||
addr = -1;
|
||||
} else {
|
||||
fprintf(stderr, "Unsupported symbol in relocation: %s\n", name);
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
ElfSection *section = elf->getSection(symtab->syms[ELF32_R_SYM(r->r_info)].st_shndx);
|
||||
assert((section->getType() == SHT_PROGBITS) && (section->getFlags() & SHF_EXECINSTR));
|
||||
addr = section->getAddr() + symtab->syms[ELF32_R_SYM(r->r_info)].st_value;
|
||||
}
|
||||
// Do the relocation
|
||||
#define REL(machine, type) (EM_ ## machine | (R_ ## machine ## _ ## type << 8))
|
||||
switch (elf->getMachine() | (ELF32_R_TYPE(r->r_info) << 8)) {
|
||||
case REL(X86_64, PC32):
|
||||
case REL(386, PC32):
|
||||
case REL(386, GOTPC):
|
||||
case REL(ARM, GOTPC):
|
||||
apply_pc32_relocation(the_code, buf, &*r, addr);
|
||||
break;
|
||||
case REL(ARM, PLT32):
|
||||
apply_arm_plt32_relocation(the_code, buf, &*r, addr);
|
||||
break;
|
||||
case REL(386, GOTOFF):
|
||||
case REL(ARM, GOTOFF):
|
||||
apply_gotoff_relocation(the_code, buf, &*r, addr);
|
||||
break;
|
||||
case REL(ARM, V4BX):
|
||||
// Ignore R_ARM_V4BX relocations
|
||||
break;
|
||||
default:
|
||||
fprintf(stderr, "Unsupported relocation type\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Elf *elf, &parent;
|
||||
std::vector<ElfSection *> code;
|
||||
ElfSection *init;
|
||||
};
|
||||
|
||||
template <typename Rel_Type>
|
||||
int do_relocation_section(Elf *elf, unsigned int rel_type)
|
||||
{
|
||||
ElfDynamic_Section *dyn = elf->getDynSection();
|
||||
if (dyn ==NULL) {
|
||||
fprintf(stderr, "Couldn't find SHT_DYNAMIC section\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
ElfRel_Section<Rel_Type> *section = (ElfRel_Section<Rel_Type> *)dyn->getSectionForType(Rel_Type::d_tag);
|
||||
assert(section->getType() == Rel_Type::sh_type);
|
||||
|
||||
Elf32_Shdr relhack32_section =
|
||||
{ 0, SHT_PROGBITS, SHF_ALLOC, 0, -1, 0, SHN_UNDEF, 0,
|
||||
Elf_RelHack::size(elf->getClass()), Elf_RelHack::size(elf->getClass()) }; // TODO: sh_addralign should be an alignment, not size
|
||||
Elf32_Shdr relhackcode32_section =
|
||||
{ 0, SHT_PROGBITS, SHF_ALLOC | SHF_EXECINSTR, 0, -1, 0, SHN_UNDEF, 0, 1, 0 };
|
||||
Elf_Shdr relhack_section(relhack32_section);
|
||||
Elf_Shdr relhackcode_section(relhackcode32_section);
|
||||
ElfRelHack_Section *relhack = new ElfRelHack_Section(relhack_section);
|
||||
ElfRelHackCode_Section *relhackcode = new ElfRelHackCode_Section(relhackcode_section, *elf);
|
||||
relhackcode->insertAfter(section);
|
||||
relhack->insertAfter(relhackcode);
|
||||
|
||||
std::vector<Rel_Type> new_rels;
|
||||
Elf_RelHack relhack_entry;
|
||||
relhack_entry.r_offset = relhack_entry.r_info = 0;
|
||||
int entry_sz = (elf->getClass() == ELFCLASS32) ? 4 : 8;
|
||||
for (typename std::vector<Rel_Type>::iterator i = section->rels.begin();
|
||||
i != section->rels.end(); i++) {
|
||||
// Don't pack relocations happening in non writable sections.
|
||||
// Our injected code is likely not to be allowed to write there.
|
||||
ElfSection *section = elf->getSectionAt(i->r_offset);
|
||||
if (!(section->getFlags() & SHF_WRITE) || (ELF32_R_TYPE(i->r_info) != rel_type))
|
||||
new_rels.push_back(*i);
|
||||
else {
|
||||
// TODO: check that i->r_addend == *i->r_offset
|
||||
if (i->r_offset == relhack_entry.r_offset + relhack_entry.r_info * entry_sz) {
|
||||
relhack_entry.r_info++;
|
||||
} else {
|
||||
if (relhack_entry.r_offset)
|
||||
relhack->push_back(relhack_entry);
|
||||
relhack_entry.r_offset = i->r_offset;
|
||||
relhack_entry.r_info = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (relhack_entry.r_offset)
|
||||
relhack->push_back(relhack_entry);
|
||||
// Last entry must be NULL
|
||||
relhack_entry.r_offset = relhack_entry.r_info = 0;
|
||||
relhack->push_back(relhack_entry);
|
||||
|
||||
section->rels.assign(new_rels.begin(), new_rels.end());
|
||||
section->shrink(new_rels.size() * section->getEntSize());
|
||||
ElfLocation *init = new ElfLocation(relhackcode, 0);
|
||||
dyn->setValueForType(DT_INIT, init);
|
||||
// TODO: adjust the value according to the remaining number of relative relocations
|
||||
dyn->setValueForType(Rel_Type::d_tag_count, new ElfPlainValue(0));
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int backup_file(const char *name)
|
||||
{
|
||||
std::string fname(name);
|
||||
fname += ".bak";
|
||||
return rename(name, fname.c_str());
|
||||
}
|
||||
|
||||
void do_file(const char *name, bool backup = false)
|
||||
{
|
||||
std::ifstream file(name, std::ios::in|std::ios::binary);
|
||||
Elf *elf = new Elf(file);
|
||||
unsigned int size = elf->getSize();
|
||||
fprintf(stderr, "%s: ", name);
|
||||
|
||||
int exit = -1;
|
||||
switch (elf->getMachine()) {
|
||||
case EM_386:
|
||||
exit = do_relocation_section<Elf_Rel>(elf, R_386_RELATIVE);
|
||||
break;
|
||||
case EM_X86_64:
|
||||
exit = do_relocation_section<Elf_Rela>(elf, R_X86_64_RELATIVE);
|
||||
break;
|
||||
case EM_ARM:
|
||||
exit = do_relocation_section<Elf_Rel>(elf, R_ARM_RELATIVE);
|
||||
break;
|
||||
}
|
||||
if (elf->getSize() >= size)
|
||||
fprintf(stderr, "No gain. Aborting\n");
|
||||
else if (exit == 0) {
|
||||
if (backup && backup_file(name) != 0) {
|
||||
fprintf(stderr, "Couln't create backup file\n");
|
||||
} else {
|
||||
std::ofstream ofile(name, std::ios::out|std::ios::binary|std::ios::trunc);
|
||||
elf->write(ofile);
|
||||
fprintf(stderr, "Reduced by %d bytes\n", size - elf->getSize());
|
||||
}
|
||||
}
|
||||
delete elf;
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
int arg;
|
||||
bool backup = false;
|
||||
char *lastSlash = rindex(argv[0], '/');
|
||||
if (lastSlash != NULL)
|
||||
rundir = strndup(argv[0], lastSlash - argv[0]);
|
||||
for (arg = 1; arg < argc; arg++) {
|
||||
if (strcmp(argv[arg], "-b") == 0)
|
||||
backup = true;
|
||||
else
|
||||
do_file(argv[arg], backup);
|
||||
}
|
||||
|
||||
free(rundir);
|
||||
return 0;
|
||||
}
|
|
@ -0,0 +1,582 @@
|
|||
/* ***** BEGIN LICENSE BLOCK *****
|
||||
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
||||
*
|
||||
* The contents of this file are subject to the Mozilla Public License Version
|
||||
* 1.1 (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
* http://www.mozilla.org/MPL/
|
||||
*
|
||||
* Software distributed under the License is distributed on an "AS IS" basis,
|
||||
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
||||
* for the specific language governing rights and limitations under the
|
||||
* License.
|
||||
*
|
||||
* The Original Code is elfhack.
|
||||
*
|
||||
* The Initial Developer of the Original Code is
|
||||
* Mozilla Foundation.
|
||||
* Portions created by the Initial Developer are Copyright (C) 2010
|
||||
* the Initial Developer. All Rights Reserved.
|
||||
*
|
||||
* Contributor(s):
|
||||
* Mike Hommey <mh@glandium.org>
|
||||
*
|
||||
* Alternatively, the contents of this file may be used under the terms of
|
||||
* either the GNU General Public License Version 2 or later (the "GPL"), or
|
||||
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
||||
* in which case the provisions of the GPL or the LGPL are applicable instead
|
||||
* of those above. If you wish to allow use of your version of this file only
|
||||
* under the terms of either the GPL or the LGPL, and not to allow others to
|
||||
* use your version of this file under the terms of the MPL, indicate your
|
||||
* decision by deleting the provisions above and replace them with the notice
|
||||
* and other provisions required by the GPL or the LGPL. If you do not delete
|
||||
* the provisions above, a recipient may use your version of this file under
|
||||
* the terms of any one of the MPL, the GPL or the LGPL.
|
||||
*
|
||||
* ***** END LICENSE BLOCK ***** */
|
||||
|
||||
#include <stdexcept>
|
||||
#include <list>
|
||||
#include <vector>
|
||||
#include <cstring>
|
||||
#include <iostream>
|
||||
#include <fstream>
|
||||
#include <elf.h>
|
||||
#include <asm/byteorder.h>
|
||||
|
||||
// Technically, __*_to_cpu and __cpu_to* function are equivalent,
|
||||
// so swap can use either of both.
|
||||
#define def_swap(endian, type, bits) \
|
||||
static inline type ## bits ## _t swap(type ## bits ## _t i) { \
|
||||
return __ ## endian ## bits ## _to_cpu(i); \
|
||||
}
|
||||
|
||||
class little_endian {
|
||||
public:
|
||||
def_swap(le, uint, 16);
|
||||
def_swap(le, uint, 32);
|
||||
def_swap(le, uint, 64);
|
||||
def_swap(le, int, 16);
|
||||
def_swap(le, int, 32);
|
||||
def_swap(le, int, 64);
|
||||
};
|
||||
|
||||
class big_endian {
|
||||
public:
|
||||
def_swap(be, uint, 16);
|
||||
def_swap(be, uint, 32);
|
||||
def_swap(be, uint, 64);
|
||||
def_swap(be, int, 16);
|
||||
def_swap(be, int, 32);
|
||||
def_swap(be, int, 64);
|
||||
};
|
||||
|
||||
// forward declaration
|
||||
class ElfSection;
|
||||
class ElfSegment;
|
||||
// TODO: Rename Elf_* types
|
||||
class Elf_Ehdr;
|
||||
class Elf_Phdr;
|
||||
class Elf;
|
||||
class ElfDynamic_Section;
|
||||
class ElfStrtab_Section;
|
||||
|
||||
class Elf_Ehdr_Traits {
|
||||
public:
|
||||
typedef Elf32_Ehdr Type32;
|
||||
typedef Elf64_Ehdr Type64;
|
||||
|
||||
template <class endian, typename R, typename T>
|
||||
static void swap(T &t, R &r);
|
||||
};
|
||||
|
||||
class Elf_Phdr_Traits {
|
||||
public:
|
||||
typedef Elf32_Phdr Type32;
|
||||
typedef Elf64_Phdr Type64;
|
||||
|
||||
template <class endian, typename R, typename T>
|
||||
static void swap(T &t, R &r);
|
||||
};
|
||||
|
||||
class Elf_Shdr_Traits {
|
||||
public:
|
||||
typedef Elf32_Shdr Type32;
|
||||
typedef Elf64_Shdr Type64;
|
||||
|
||||
template <class endian, typename R, typename T>
|
||||
static void swap(T &t, R &r);
|
||||
};
|
||||
|
||||
class Elf_Dyn_Traits {
|
||||
public:
|
||||
typedef Elf32_Dyn Type32;
|
||||
typedef Elf64_Dyn Type64;
|
||||
|
||||
template <class endian, typename R, typename T>
|
||||
static void swap(T &t, R &r);
|
||||
};
|
||||
|
||||
class Elf_Sym_Traits {
|
||||
public:
|
||||
typedef Elf32_Sym Type32;
|
||||
typedef Elf64_Sym Type64;
|
||||
|
||||
template <class endian, typename R, typename T>
|
||||
static void swap(T &t, R &r);
|
||||
};
|
||||
|
||||
class Elf_Rel_Traits {
|
||||
public:
|
||||
typedef Elf32_Rel Type32;
|
||||
typedef Elf64_Rel Type64;
|
||||
|
||||
template <class endian, typename R, typename T>
|
||||
static void swap(T &t, R &r);
|
||||
};
|
||||
|
||||
class Elf_Rela_Traits {
|
||||
public:
|
||||
typedef Elf32_Rela Type32;
|
||||
typedef Elf64_Rela Type64;
|
||||
|
||||
template <class endian, typename R, typename T>
|
||||
static void swap(T &t, R &r);
|
||||
};
|
||||
|
||||
class ElfValue {
|
||||
public:
|
||||
virtual unsigned int getValue() { return 0; }
|
||||
virtual ElfSection *getSection() { return NULL; }
|
||||
};
|
||||
|
||||
class ElfPlainValue: public ElfValue {
|
||||
unsigned int value;
|
||||
public:
|
||||
ElfPlainValue(unsigned int val): value(val) {};
|
||||
unsigned int getValue() { return value; }
|
||||
};
|
||||
|
||||
class ElfLocation: public ElfValue {
|
||||
ElfSection *section;
|
||||
unsigned int offset;
|
||||
public:
|
||||
ElfLocation(): section(NULL), offset(0) {};
|
||||
ElfLocation(ElfSection *section, unsigned int offset): section(section), offset(offset) {};
|
||||
ElfLocation(unsigned int location, Elf *elf);
|
||||
unsigned int getValue();
|
||||
ElfSection *getSection() { return section; }
|
||||
};
|
||||
|
||||
class ElfSize: public ElfValue {
|
||||
ElfSection *section;
|
||||
public:
|
||||
ElfSize(ElfSection *s): section(s) {};
|
||||
unsigned int getValue();
|
||||
ElfSection *getSection() { return section; }
|
||||
};
|
||||
|
||||
class ElfEntSize: public ElfValue {
|
||||
ElfSection *section;
|
||||
public:
|
||||
ElfEntSize(ElfSection *s): section(s) {};
|
||||
unsigned int getValue();
|
||||
ElfSection *getSection() { return section; }
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
class serializable: public T::Type32 {
|
||||
public:
|
||||
serializable() {};
|
||||
serializable(const typename T::Type32 &p): T::Type32(p) {};
|
||||
serializable(std::ifstream &file, char ei_class, char ei_data)
|
||||
{
|
||||
if (ei_class == ELFCLASS32) {
|
||||
typename T::Type32 e;
|
||||
file.read((char *)&e, sizeof(e));
|
||||
if (ei_data == ELFDATA2LSB) {
|
||||
T::template swap<little_endian>(e, *this);
|
||||
return;
|
||||
} else if (ei_data == ELFDATA2MSB) {
|
||||
T::template swap<big_endian>(e, *this);
|
||||
return;
|
||||
}
|
||||
} else if (ei_class == ELFCLASS64) {
|
||||
typename T::Type64 e;
|
||||
file.read((char *)&e, sizeof(e));
|
||||
if (ei_data == ELFDATA2LSB) {
|
||||
T::template swap<little_endian>(e, *this);
|
||||
return;
|
||||
} else if (ei_data == ELFDATA2MSB) {
|
||||
T::template swap<big_endian>(e, *this);
|
||||
return;
|
||||
}
|
||||
}
|
||||
throw std::runtime_error("Unsupported ELF class or data encoding");
|
||||
}
|
||||
|
||||
void serialize(std::ofstream &file, char ei_class, char ei_data)
|
||||
{
|
||||
if (ei_class == ELFCLASS32) {
|
||||
typename T::Type32 e;
|
||||
if (ei_data == ELFDATA2LSB) {
|
||||
T::template swap<little_endian>(*this, e);
|
||||
file.write((char *)&e, sizeof(e));
|
||||
return;
|
||||
} else if (ei_data == ELFDATA2MSB) {
|
||||
T::template swap<big_endian>(*this, e);
|
||||
file.write((char *)&e, sizeof(e));
|
||||
return;
|
||||
}
|
||||
} else if (ei_class == ELFCLASS64) {
|
||||
typename T::Type64 e;
|
||||
if (ei_data == ELFDATA2LSB) {
|
||||
T::template swap<little_endian>(*this, e);
|
||||
file.write((char *)&e, sizeof(e));
|
||||
return;
|
||||
} else if (ei_data == ELFDATA2MSB) {
|
||||
T::template swap<big_endian>(*this, e);
|
||||
file.write((char *)&e, sizeof(e));
|
||||
return;
|
||||
}
|
||||
}
|
||||
throw std::runtime_error("Unsupported ELF class or data encoding");
|
||||
}
|
||||
|
||||
static inline int size(char ei_class)
|
||||
{
|
||||
if (ei_class == ELFCLASS32)
|
||||
return sizeof(typename T::Type32);
|
||||
else if (ei_class == ELFCLASS64)
|
||||
return sizeof(typename T::Type64);
|
||||
return 0;
|
||||
}
|
||||
};
|
||||
|
||||
typedef serializable<Elf_Shdr_Traits> Elf_Shdr;
|
||||
|
||||
class Elf {
|
||||
public:
|
||||
Elf(std::ifstream &file);
|
||||
~Elf();
|
||||
|
||||
/* index == -1 is treated as index == ehdr.e_shstrndx */
|
||||
ElfSection *getSection(int index);
|
||||
|
||||
ElfSection *getSectionAt(unsigned int offset);
|
||||
|
||||
ElfDynamic_Section *getDynSection();
|
||||
|
||||
void write(std::ofstream &file);
|
||||
|
||||
char getClass();
|
||||
char getData();
|
||||
char getType();
|
||||
char getMachine();
|
||||
unsigned int getSize();
|
||||
private:
|
||||
Elf_Ehdr *ehdr;
|
||||
ElfLocation eh_entry;
|
||||
ElfStrtab_Section *eh_shstrndx;
|
||||
ElfSection **sections;
|
||||
std::vector<ElfSegment *> segments;
|
||||
ElfSection *shdr_section, *phdr_section;
|
||||
/* Values used only during initialization */
|
||||
Elf_Shdr **tmp_shdr;
|
||||
std::ifstream *tmp_file;
|
||||
};
|
||||
|
||||
class ElfSection {
|
||||
public:
|
||||
typedef union {
|
||||
ElfSection *section;
|
||||
int index;
|
||||
} SectionInfo;
|
||||
|
||||
ElfSection(Elf_Shdr &s, std::ifstream *file, Elf *parent);
|
||||
|
||||
virtual ~ElfSection() {
|
||||
delete[] data;
|
||||
}
|
||||
|
||||
const char *getName() { return name; }
|
||||
unsigned int getType() { return shdr.sh_type; }
|
||||
unsigned int getFlags() { return shdr.sh_flags; }
|
||||
unsigned int getAddr();
|
||||
unsigned int getSize() { return shdr.sh_size; }
|
||||
unsigned int getAddrAlign() { return shdr.sh_addralign; }
|
||||
unsigned int getEntSize() { return shdr.sh_entsize; }
|
||||
const char *getData() { return data; }
|
||||
ElfSection *getLink() { return link; }
|
||||
SectionInfo getInfo() { return info; }
|
||||
|
||||
void shrink(unsigned int newsize) {
|
||||
if (newsize < shdr.sh_size) {
|
||||
shdr.sh_size = newsize;
|
||||
if (next)
|
||||
next->markDirty();
|
||||
}
|
||||
}
|
||||
|
||||
unsigned int getOffset();
|
||||
int getIndex();
|
||||
Elf_Shdr &getShdr();
|
||||
|
||||
ElfSection *getNext() { return next; }
|
||||
ElfSection *getPrevious() { return previous; }
|
||||
|
||||
virtual bool isRelocatable() {
|
||||
return ((getType() != SHT_NULL) &&
|
||||
(getType() != SHT_NOBITS) &&
|
||||
(getType() != SHT_PROGBITS) &&
|
||||
(getFlags() == SHF_ALLOC));
|
||||
}
|
||||
|
||||
void insertAfter(ElfSection *section) {
|
||||
if (previous != NULL)
|
||||
previous->next = next;
|
||||
if (next != NULL)
|
||||
next->previous = previous;
|
||||
previous = section;
|
||||
if (section != NULL) {
|
||||
next = section->next;
|
||||
section->next = this;
|
||||
} else
|
||||
next = NULL;
|
||||
if (next != NULL)
|
||||
next->previous = this;
|
||||
markDirty();
|
||||
}
|
||||
|
||||
void markDirty() {
|
||||
if (link != NULL)
|
||||
shdr.sh_link = -1;
|
||||
if (info.index)
|
||||
shdr.sh_info = -1;
|
||||
shdr.sh_offset = -1;
|
||||
if (isRelocatable())
|
||||
shdr.sh_addr = -1;
|
||||
if (next)
|
||||
next->markDirty();
|
||||
}
|
||||
|
||||
virtual void serialize(std::ofstream &file, char ei_class, char ei_data)
|
||||
{
|
||||
if (getType() == SHT_NOBITS)
|
||||
return;
|
||||
file.seekp(getOffset());
|
||||
file.write(data, getSize());
|
||||
}
|
||||
protected:
|
||||
Elf_Shdr shdr;
|
||||
char *data;
|
||||
const char *name;
|
||||
private:
|
||||
ElfSection *link;
|
||||
SectionInfo info;
|
||||
ElfSection *next, *previous;
|
||||
int index;
|
||||
};
|
||||
|
||||
class ElfSegment {
|
||||
public:
|
||||
ElfSegment(Elf_Phdr *phdr);
|
||||
|
||||
unsigned int getType() { return type; }
|
||||
unsigned int getFlags() { return flags; }
|
||||
unsigned int getAlign() { return type == PT_LOAD ? 0x1000 : align; /* TODO: remove this gross hack */ }
|
||||
|
||||
ElfSection *getFirstSection() { return sections.empty() ? NULL : sections.front(); }
|
||||
int getVPDiff() { return v_p_diff; }
|
||||
unsigned int getFileSize();
|
||||
unsigned int getMemSize();
|
||||
|
||||
void addSection(ElfSection *section);
|
||||
|
||||
std::list<ElfSection *>::iterator begin() { return sections.begin(); }
|
||||
std::list<ElfSection *>::iterator end() { return sections.end(); }
|
||||
|
||||
ElfSegment *splitBefore(ElfSection *section);
|
||||
private:
|
||||
unsigned int type;
|
||||
int v_p_diff; // Difference between physical and virtual address
|
||||
unsigned int flags;
|
||||
unsigned int align;
|
||||
std::list<ElfSection *> sections;
|
||||
};
|
||||
|
||||
class Elf_Ehdr: public serializable<Elf_Ehdr_Traits>, public ElfSection {
|
||||
public:
|
||||
Elf_Ehdr(std::ifstream &file, char ei_class, char ei_data);
|
||||
void serialize(std::ofstream &file, char ei_class, char ei_data)
|
||||
{
|
||||
serializable<Elf_Ehdr_Traits>::serialize(file, ei_class, ei_data);
|
||||
}
|
||||
};
|
||||
|
||||
class Elf_Phdr: public serializable<Elf_Phdr_Traits> {
|
||||
public:
|
||||
Elf_Phdr() {};
|
||||
Elf_Phdr(std::ifstream &file, char ei_class, char ei_data)
|
||||
: serializable<Elf_Phdr_Traits>(file, ei_class, ei_data) {};
|
||||
bool contains(ElfSection *section)
|
||||
{
|
||||
unsigned int size = section->getSize();
|
||||
unsigned int addr = section->getAddr();
|
||||
// This may be biased, but should work in most cases
|
||||
if ((section->getFlags() & SHF_ALLOC) == 0)
|
||||
return false;
|
||||
return (addr >= p_vaddr) &&
|
||||
(addr + size <= p_vaddr + p_memsz);
|
||||
|
||||
}
|
||||
};
|
||||
|
||||
typedef serializable<Elf_Dyn_Traits> Elf_Dyn;
|
||||
|
||||
struct Elf_DynValue {
|
||||
unsigned int tag;
|
||||
ElfValue *value;
|
||||
};
|
||||
|
||||
class ElfDynamic_Section: public ElfSection {
|
||||
public:
|
||||
ElfDynamic_Section(Elf_Shdr &s, std::ifstream *file, Elf *parent);
|
||||
~ElfDynamic_Section();
|
||||
|
||||
void serialize(std::ofstream &file, char ei_class, char ei_data);
|
||||
|
||||
ElfSection *getSectionForType(unsigned int tag);
|
||||
void setValueForType(unsigned int tag, ElfValue *val);
|
||||
private:
|
||||
std::vector<Elf_DynValue> dyns;
|
||||
};
|
||||
|
||||
typedef serializable<Elf_Sym_Traits> Elf_Sym;
|
||||
|
||||
class ElfSymtab_Section: public ElfSection {
|
||||
public:
|
||||
ElfSymtab_Section(Elf_Shdr &s, std::ifstream *file, Elf *parent);
|
||||
|
||||
//private: // Until we have a real API
|
||||
std::vector<Elf_Sym> syms;
|
||||
};
|
||||
|
||||
class Elf_Rel: public serializable<Elf_Rel_Traits> {
|
||||
public:
|
||||
Elf_Rel(std::ifstream &file, char ei_class, char ei_data)
|
||||
: serializable<Elf_Rel_Traits>(file, ei_class, ei_data) {};
|
||||
|
||||
static const unsigned int sh_type = SHT_REL;
|
||||
static const unsigned int d_tag = DT_REL;
|
||||
static const unsigned int d_tag_count = DT_RELCOUNT;
|
||||
};
|
||||
|
||||
class Elf_Rela: public serializable<Elf_Rela_Traits> {
|
||||
public:
|
||||
Elf_Rela(std::ifstream &file, char ei_class, char ei_data)
|
||||
: serializable<Elf_Rela_Traits>(file, ei_class, ei_data) {};
|
||||
|
||||
static const unsigned int sh_type = SHT_RELA;
|
||||
static const unsigned int d_tag = DT_RELA;
|
||||
static const unsigned int d_tag_count = DT_RELACOUNT;
|
||||
};
|
||||
|
||||
template <class Rel>
|
||||
class ElfRel_Section: public ElfSection {
|
||||
public:
|
||||
ElfRel_Section(Elf_Shdr &s, std::ifstream *file, Elf *parent)
|
||||
: ElfSection(s, file, parent)
|
||||
{
|
||||
int pos = file->tellg();
|
||||
file->seekg(shdr.sh_offset);
|
||||
for (unsigned int i = 0; i < s.sh_size / s.sh_entsize; i++) {
|
||||
Rel r(*file, parent->getClass(), parent->getData());
|
||||
rels.push_back(r);
|
||||
}
|
||||
file->seekg(pos);
|
||||
}
|
||||
|
||||
void serialize(std::ofstream &file, char ei_class, char ei_data)
|
||||
{
|
||||
for (typename std::vector<Rel>::iterator i = rels.begin();
|
||||
i != rels.end(); ++i)
|
||||
(*i).serialize(file, ei_class, ei_data);
|
||||
}
|
||||
//private: // Until we have a real API
|
||||
std::vector<Rel> rels;
|
||||
};
|
||||
|
||||
class ElfStrtab_Section: public ElfSection {
|
||||
public:
|
||||
ElfStrtab_Section(Elf_Shdr &s, std::ifstream *file, Elf *parent)
|
||||
: ElfSection(s, file, parent)
|
||||
{
|
||||
table.push_back(table_storage(data, shdr.sh_size));
|
||||
}
|
||||
|
||||
~ElfStrtab_Section()
|
||||
{
|
||||
for (std::vector<table_storage>::iterator t = table.begin() + 1;
|
||||
t != table.end(); t++)
|
||||
delete[] t->buf;
|
||||
}
|
||||
|
||||
const char *getStr(unsigned int index);
|
||||
|
||||
const char *getStr(const char *string);
|
||||
|
||||
unsigned int getStrIndex(const char *string);
|
||||
|
||||
void serialize(std::ofstream &file, char ei_class, char ei_data);
|
||||
private:
|
||||
struct table_storage {
|
||||
unsigned int size, used;
|
||||
char *buf;
|
||||
|
||||
table_storage(): size(4096), used(0), buf(new char[4096]) {}
|
||||
table_storage(const char *data, unsigned int sz)
|
||||
: size(sz), used(sz), buf(const_cast<char *>(data)) {}
|
||||
};
|
||||
std::vector<table_storage> table;
|
||||
};
|
||||
|
||||
inline char Elf::getClass() {
|
||||
return ehdr->e_ident[EI_CLASS];
|
||||
}
|
||||
|
||||
inline char Elf::getData() {
|
||||
return ehdr->e_ident[EI_DATA];
|
||||
}
|
||||
|
||||
inline char Elf::getType() {
|
||||
return ehdr->e_type;
|
||||
}
|
||||
|
||||
inline char Elf::getMachine() {
|
||||
return ehdr->e_machine;
|
||||
}
|
||||
|
||||
inline unsigned int Elf::getSize() {
|
||||
ElfSection *section;
|
||||
for (section = shdr_section /* It's usually not far from the end */;
|
||||
section->getNext() != NULL; section = section->getNext());
|
||||
return section->getOffset() + section->getSize();
|
||||
}
|
||||
|
||||
inline ElfLocation::ElfLocation(unsigned int location, Elf *elf) {
|
||||
section = elf->getSectionAt(location);
|
||||
offset = location - section->getAddr();
|
||||
}
|
||||
|
||||
inline unsigned int ElfLocation::getValue() {
|
||||
return section->getAddr() + offset;
|
||||
}
|
||||
|
||||
inline unsigned int ElfSize::getValue() {
|
||||
return section->getSize();
|
||||
}
|
||||
|
||||
inline unsigned int ElfEntSize::getValue() {
|
||||
return section->getEntSize();
|
||||
}
|
|
@ -0,0 +1,71 @@
|
|||
/* ***** BEGIN LICENSE BLOCK *****
|
||||
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
||||
*
|
||||
* The contents of this file are subject to the Mozilla Public License Version
|
||||
* 1.1 (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
* http://www.mozilla.org/MPL/
|
||||
*
|
||||
* Software distributed under the License is distributed on an "AS IS" basis,
|
||||
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
||||
* for the specific language governing rights and limitations under the
|
||||
* License.
|
||||
*
|
||||
* The Original Code is elfhack.
|
||||
*
|
||||
* The Initial Developer of the Original Code is
|
||||
* Mozilla Foundation.
|
||||
* Portions created by the Initial Developer are Copyright (C) 2010
|
||||
* the Initial Developer. All Rights Reserved.
|
||||
*
|
||||
* Contributor(s):
|
||||
* Mike Hommey <mh@glandium.org>
|
||||
*
|
||||
* Alternatively, the contents of this file may be used under the terms of
|
||||
* either the GNU General Public License Version 2 or later (the "GPL"), or
|
||||
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
||||
* in which case the provisions of the GPL or the LGPL are applicable instead
|
||||
* of those above. If you wish to allow use of your version of this file only
|
||||
* under the terms of either the GPL or the LGPL, and not to allow others to
|
||||
* use your version of this file under the terms of the MPL, indicate your
|
||||
* decision by deleting the provisions above and replace them with the notice
|
||||
* and other provisions required by the GPL or the LGPL. If you do not delete
|
||||
* the provisions above, a recipient may use your version of this file under
|
||||
* the terms of any one of the MPL, the GPL or the LGPL.
|
||||
*
|
||||
* ***** END LICENSE BLOCK ***** */
|
||||
|
||||
#include <stdint.h>
|
||||
#include <elf.h>
|
||||
|
||||
/* The Android NDK headers define those */
|
||||
#undef Elf_Ehdr
|
||||
#undef Elf_Addr
|
||||
|
||||
#if BITS == 32
|
||||
#define Elf_Ehdr Elf32_Ehdr
|
||||
#define Elf_Addr Elf32_Addr
|
||||
#else
|
||||
#define Elf_Ehdr Elf64_Ehdr
|
||||
#define Elf_Addr Elf64_Addr
|
||||
#endif
|
||||
|
||||
extern __attribute__((visibility("hidden"))) void original_init(int argc, char **argv, char **env);
|
||||
|
||||
extern __attribute__((visibility("hidden"))) Elf32_Rel relhack[];
|
||||
extern __attribute__((visibility("hidden"))) Elf_Ehdr elf_header;
|
||||
|
||||
void init(int argc, char **argv, char **env)
|
||||
{
|
||||
Elf32_Rel *rel;
|
||||
Elf_Addr *ptr, *start;
|
||||
for (rel = relhack; rel->r_offset; rel++) {
|
||||
start = (Elf_Addr *)((intptr_t)&elf_header + rel->r_offset);
|
||||
for (ptr = start; ptr < &start[rel->r_info]; ptr++)
|
||||
*ptr += (intptr_t)&elf_header;
|
||||
}
|
||||
|
||||
#ifndef NOINIT
|
||||
original_init(argc, argv, env);
|
||||
#endif
|
||||
}
|
|
@ -0,0 +1,148 @@
|
|||
#ifdef DEF
|
||||
DEF(This)
|
||||
DEF(is)
|
||||
DEF(a)
|
||||
DEF(test)
|
||||
DEF(of)
|
||||
DEF(string)
|
||||
DEF(array)
|
||||
DEF(for)
|
||||
DEF(use)
|
||||
DEF(with)
|
||||
DEF(elfhack)
|
||||
DEF(to)
|
||||
DEF(see)
|
||||
DEF(whether)
|
||||
DEF(it)
|
||||
DEF(breaks)
|
||||
DEF(anything)
|
||||
DEF(but)
|
||||
DEF(one)
|
||||
DEF(needs)
|
||||
DEF(quite)
|
||||
DEF(some)
|
||||
DEF(strings)
|
||||
DEF(before)
|
||||
DEF(the)
|
||||
DEF(program)
|
||||
DEF(can)
|
||||
DEF(do)
|
||||
DEF(its)
|
||||
DEF(work)
|
||||
DEF(efficiently)
|
||||
DEF(Without)
|
||||
DEF(enough)
|
||||
DEF(data)
|
||||
DEF(relocation)
|
||||
DEF(sections)
|
||||
DEF(are)
|
||||
DEF(not)
|
||||
DEF(sufficiently)
|
||||
DEF(large)
|
||||
DEF(and)
|
||||
DEF(injected)
|
||||
DEF(code)
|
||||
DEF(wouldnt)
|
||||
DEF(fit)
|
||||
DEF(Said)
|
||||
DEF(otherwise)
|
||||
DEF(we)
|
||||
DEF(need)
|
||||
DEF(more)
|
||||
DEF(words)
|
||||
DEF(than)
|
||||
DEF(up)
|
||||
DEF(here)
|
||||
DEF(so)
|
||||
DEF(that)
|
||||
DEF(relocations)
|
||||
DEF(take)
|
||||
DEF(significant)
|
||||
DEF(bytes)
|
||||
DEF(amounts)
|
||||
DEF(which)
|
||||
DEF(isnt)
|
||||
DEF(exactly)
|
||||
DEF(easily)
|
||||
DEF(achieved)
|
||||
DEF(like)
|
||||
DEF(this)
|
||||
DEF(Actually)
|
||||
DEF(I)
|
||||
DEF(must)
|
||||
DEF(cheat)
|
||||
DEF(by)
|
||||
DEF(including)
|
||||
DEF(these)
|
||||
DEF(phrases)
|
||||
DEF(several)
|
||||
DEF(times)
|
||||
|
||||
#else
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#define DEF(w) static const char str_ ## w[] = #w;
|
||||
#include "test.c"
|
||||
#undef DEF
|
||||
|
||||
const char *strings[] = {
|
||||
#define DEF(w) str_ ## w,
|
||||
#include "test.c"
|
||||
#include "test.c"
|
||||
#include "test.c"
|
||||
};
|
||||
|
||||
/* Create a hole between two zones of relative relocations */
|
||||
const int hole[] = {
|
||||
42, 42, 42, 42
|
||||
};
|
||||
|
||||
const char *strings2[] = {
|
||||
#include "test.c"
|
||||
#include "test.c"
|
||||
#include "test.c"
|
||||
#include "test.c"
|
||||
#include "test.c"
|
||||
#undef DEF
|
||||
};
|
||||
|
||||
/* On ARM, this creates a .tbss section before .init_array, which
|
||||
* elfhack could then pick instead of .init_array */
|
||||
__thread int foo;
|
||||
|
||||
__attribute__((constructor)) void end_test() {
|
||||
static int count = 0;
|
||||
// Only exit when both constructors have been called
|
||||
if (++count == 2) {
|
||||
fprintf(stderr, "PASS\n");
|
||||
exit(0);
|
||||
}
|
||||
}
|
||||
|
||||
__attribute__((constructor)) void test() {
|
||||
int i = 0, j = 0;
|
||||
#define DEF_(a,i,w) \
|
||||
if (a[i++] != str_ ## w) { \
|
||||
fprintf(stderr, "FAIL\n"); \
|
||||
exit(1); \
|
||||
}
|
||||
#define DEF(w) DEF_(strings,i,w)
|
||||
#include "test.c"
|
||||
#include "test.c"
|
||||
#include "test.c"
|
||||
#undef DEF
|
||||
#define DEF(w) DEF_(strings2,j,w)
|
||||
#include "test.c"
|
||||
#include "test.c"
|
||||
#include "test.c"
|
||||
#include "test.c"
|
||||
#include "test.c"
|
||||
#undef DEF
|
||||
if (i != sizeof(strings)/sizeof(strings[0]) &&
|
||||
j != sizeof(strings2)/sizeof(strings2[0]))
|
||||
fprintf(stderr, "WARNING: Test doesn't cover the whole array\n");
|
||||
end_test();
|
||||
}
|
||||
|
||||
#endif
|
|
@ -117,6 +117,7 @@ DEHYDRA_PATH = @DEHYDRA_PATH@
|
|||
|
||||
NS_TRACE_MALLOC = @NS_TRACE_MALLOC@
|
||||
USE_ELF_DYNSTR_GC = @USE_ELF_DYNSTR_GC@
|
||||
USE_ELF_HACK = @USE_ELF_HACK@
|
||||
INCREMENTAL_LINKER = @INCREMENTAL_LINKER@
|
||||
MACOSX_DEPLOYMENT_TARGET = @MACOSX_DEPLOYMENT_TARGET@
|
||||
MOZ_MAIL_NEWS = @MOZ_MAIL_NEWS@
|
||||
|
|
|
@ -556,6 +556,9 @@ TAG_PROGRAM = xargs etags -a
|
|||
ifneq ($(CPPSRCS)$(CMMSRCS),)
|
||||
CPP_PROG_LINK = 1
|
||||
endif
|
||||
ifneq ($(HOST_CPPSRCS)$(HOST_CMMSRCS),)
|
||||
HOST_CPP_PROG_LINK = 1
|
||||
endif
|
||||
|
||||
#
|
||||
# Make sure to wrap static libs inside linker specific flags to turn on & off
|
||||
|
@ -1085,11 +1088,11 @@ ifdef MSMANIFEST_TOOL
|
|||
fi
|
||||
endif # MSVC with manifest tool
|
||||
else
|
||||
ifeq ($(CPP_PROG_LINK),1)
|
||||
ifeq ($(HOST_CPP_PROG_LINK),1)
|
||||
$(HOST_CXX) -o $@ $(HOST_CXXFLAGS) $(HOST_LDFLAGS) $(HOST_PROGOBJS) $(HOST_LIBS) $(HOST_EXTRA_LIBS)
|
||||
else
|
||||
$(HOST_CC) -o $@ $(HOST_CFLAGS) $(HOST_LDFLAGS) $(HOST_PROGOBJS) $(HOST_LIBS) $(HOST_EXTRA_LIBS)
|
||||
endif # CPP_PROG_LINK
|
||||
endif # HOST_CPP_PROG_LINK
|
||||
endif
|
||||
endif
|
||||
|
||||
|
|
11
configure.in
11
configure.in
|
@ -7760,6 +7760,16 @@ MOZ_ARG_ENABLE_BOOL(elf-dynstr-gc,
|
|||
USE_ELF_DYNSTR_GC=1,
|
||||
USE_ELF_DYNSTR_GC= )
|
||||
|
||||
dnl ========================================================
|
||||
dnl = --disable-elf-hack
|
||||
dnl ========================================================
|
||||
|
||||
USE_ELF_HACK=1
|
||||
MOZ_ARG_DISABLE_BOOL(elf-hack,
|
||||
[ --disable-elf-hack Disable elf hacks],
|
||||
USE_ELF_HACK=,
|
||||
USE_ELF_HACK=1)
|
||||
|
||||
dnl ========================================================
|
||||
dnl =
|
||||
dnl = Profiling and Instrumenting
|
||||
|
@ -9035,6 +9045,7 @@ AC_SUBST(MOZ_DIRECTX_SDK_PATH)
|
|||
AC_SUBST(ENABLE_STRIP)
|
||||
AC_SUBST(PKG_SKIP_STRIP)
|
||||
AC_SUBST(USE_ELF_DYNSTR_GC)
|
||||
AC_SUBST(USE_ELF_HACK)
|
||||
AC_SUBST(INCREMENTAL_LINKER)
|
||||
AC_SUBST(MOZ_COMPONENTS_VERSION_SCRIPT_LDFLAGS)
|
||||
AC_SUBST(MOZ_JEMALLOC_STANDALONE_GLUE_LDOPTS)
|
||||
|
|
|
@ -1315,6 +1315,7 @@ nsEventListenerManager::DispatchEvent(nsIDOMEvent* aEvent, PRBool *_retval)
|
|||
|
||||
// Do nothing if the element does not belong to a document
|
||||
if (!document) {
|
||||
*_retval = PR_TRUE;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
|
|
|
@ -2961,6 +2961,24 @@ nsEventStateManager::PostHandleEvent(nsPresContext* aPresContext,
|
|||
// if we're here, the event handler returned false, so stop
|
||||
// any of our own processing of a drag. Workaround for bug 43258.
|
||||
StopTrackingDragGesture();
|
||||
|
||||
// When the event was cancelled, there is currently a chrome document
|
||||
// focused and a mousedown just occurred on a content document, ensure
|
||||
// that the window that was clicked is focused.
|
||||
EnsureDocument(mPresContext);
|
||||
nsIFocusManager* fm = nsFocusManager::GetFocusManager();
|
||||
if (mDocument && fm) {
|
||||
nsCOMPtr<nsIDOMWindow> currentWindow;
|
||||
fm->GetFocusedWindow(getter_AddRefs(currentWindow));
|
||||
if (currentWindow && currentWindow != mDocument->GetWindow() &&
|
||||
!nsContentUtils::IsChromeDoc(mDocument)) {
|
||||
nsCOMPtr<nsPIDOMWindow> win = do_QueryInterface(currentWindow);
|
||||
nsCOMPtr<nsIDocument> currentDoc = do_QueryInterface(win->GetExtantDocument());
|
||||
if (nsContentUtils::IsChromeDoc(currentDoc)) {
|
||||
fm->SetFocusedWindow(mDocument->GetWindow());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
SetActiveManager(this, activeContent);
|
||||
}
|
||||
|
|
|
@ -799,6 +799,7 @@ nsGlobalWindow::nsGlobalWindow(nsGlobalWindow *aOuterWindow)
|
|||
mFireOfflineStatusChangeEventOnThaw(PR_FALSE),
|
||||
mCreatingInnerWindow(PR_FALSE),
|
||||
mIsChrome(PR_FALSE),
|
||||
mCleanMessageManager(PR_FALSE),
|
||||
mNeedsFocus(PR_TRUE),
|
||||
mHasFocus(PR_FALSE),
|
||||
#if defined(XP_MAC) || defined(XP_MACOSX)
|
||||
|
@ -1117,10 +1118,13 @@ nsGlobalWindow::CleanUp(PRBool aIgnoreModalDialog)
|
|||
DisableAccelerationUpdates();
|
||||
mHasAcceleration = PR_FALSE;
|
||||
|
||||
if (mIsChrome && static_cast<nsGlobalChromeWindow*>(this)->mMessageManager) {
|
||||
static_cast<nsFrameMessageManager*>(
|
||||
static_cast<nsGlobalChromeWindow*>(
|
||||
this)->mMessageManager.get())->Disconnect();
|
||||
if (mCleanMessageManager) {
|
||||
NS_ABORT_IF_FALSE(mIsChrome, "only chrome should have msg manager cleaned");
|
||||
nsGlobalChromeWindow *asChrome = static_cast<nsGlobalChromeWindow*>(this);
|
||||
if (asChrome->mMessageManager) {
|
||||
static_cast<nsFrameMessageManager*>(
|
||||
asChrome->mMessageManager.get())->Disconnect();
|
||||
}
|
||||
}
|
||||
|
||||
mInnerWindowHolder = nsnull;
|
||||
|
@ -2075,9 +2079,9 @@ nsGlobalWindow::SetNewDocument(nsIDocument* aDocument,
|
|||
}
|
||||
|
||||
JS_SetParent(cx, mJSObject, newInnerWindow->mJSObject);
|
||||
}
|
||||
|
||||
mContext->SetOuterObject(mJSObject);
|
||||
mContext->SetOuterObject(mJSObject);
|
||||
}
|
||||
}
|
||||
|
||||
JSAutoEnterCompartment ac;
|
||||
|
@ -2110,6 +2114,12 @@ nsGlobalWindow::SetNewDocument(nsIDocument* aDocument,
|
|||
}
|
||||
}
|
||||
|
||||
JSAutoEnterCompartment ac;
|
||||
if (!ac.enter(cx, mJSObject)) {
|
||||
NS_ERROR("unable to enter a compartment");
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
if (!aState && !reUseInnerWindow) {
|
||||
// Loading a new page and creating a new inner window, *not*
|
||||
// restoring from session history.
|
||||
|
|
|
@ -863,6 +863,11 @@ protected:
|
|||
// Fast way to tell if this is a chrome window (without having to QI).
|
||||
PRPackedBool mIsChrome : 1;
|
||||
|
||||
// Hack to indicate whether a chrome window needs its message manager
|
||||
// to be disconnected, since clean up code is shared in the global
|
||||
// window superclass.
|
||||
PRPackedBool mCleanMessageManager : 1;
|
||||
|
||||
// Indicates that the current document has never received a document focus
|
||||
// event.
|
||||
PRPackedBool mNeedsFocus : 1;
|
||||
|
@ -1004,6 +1009,19 @@ public:
|
|||
: nsGlobalWindow(aOuterWindow)
|
||||
{
|
||||
mIsChrome = PR_TRUE;
|
||||
mCleanMessageManager = PR_TRUE;
|
||||
}
|
||||
|
||||
~nsGlobalChromeWindow()
|
||||
{
|
||||
NS_ABORT_IF_FALSE(mCleanMessageManager,
|
||||
"chrome windows may always disconnect the msg manager");
|
||||
if (mMessageManager) {
|
||||
static_cast<nsFrameMessageManager *>(
|
||||
mMessageManager.get())->Disconnect();
|
||||
}
|
||||
|
||||
mCleanMessageManager = PR_FALSE;
|
||||
}
|
||||
|
||||
NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED_NO_UNLINK(nsGlobalChromeWindow,
|
||||
|
|
|
@ -750,14 +750,9 @@ IDBDatabase::Transaction(nsIVariant* aStoreNames,
|
|||
|
||||
if (aOptionalArgCount) {
|
||||
if (aMode != nsIIDBTransaction::READ_WRITE &&
|
||||
aMode != nsIIDBTransaction::READ_ONLY &&
|
||||
aMode != nsIIDBTransaction::SNAPSHOT_READ) {
|
||||
aMode != nsIIDBTransaction::READ_ONLY) {
|
||||
return NS_ERROR_DOM_INDEXEDDB_NON_TRANSIENT_ERR;
|
||||
}
|
||||
if (aMode == nsIIDBTransaction::SNAPSHOT_READ) {
|
||||
NS_NOTYETIMPLEMENTED("Implement me!");
|
||||
return NS_ERROR_NOT_IMPLEMENTED;
|
||||
}
|
||||
}
|
||||
else {
|
||||
aMode = nsIIDBTransaction::READ_ONLY;
|
||||
|
|
|
@ -94,7 +94,7 @@ public:
|
|||
|
||||
const nsString& KeyPath() const
|
||||
{
|
||||
return mName;
|
||||
return mKeyPath;
|
||||
}
|
||||
|
||||
private:
|
||||
|
|
|
@ -251,9 +251,6 @@ private:
|
|||
|
||||
// In-params.
|
||||
nsRefPtr<IDBIndex> mIndex;
|
||||
|
||||
// Out-params.
|
||||
PRInt64 mId;
|
||||
};
|
||||
|
||||
class DeleteIndexHelper : public AsyncConnectionHelper
|
||||
|
@ -2129,8 +2126,13 @@ CreateIndexHelper::DoDatabaseWork(mozIStorageConnection* aConnection)
|
|||
return NS_ERROR_DOM_INDEXEDDB_CONSTRAINT_ERR;
|
||||
}
|
||||
|
||||
// Get the id of this object store, and store it for future use.
|
||||
(void)aConnection->GetLastInsertRowID(&mId);
|
||||
#ifdef DEBUG
|
||||
{
|
||||
PRInt64 id;
|
||||
aConnection->GetLastInsertRowID(&id);
|
||||
NS_ASSERTION(mIndex->Id() == id, "Bad index id!");
|
||||
}
|
||||
#endif
|
||||
|
||||
// Now we need to populate the index with data from the object store.
|
||||
rv = InsertDataFromObjectStore(aConnection);
|
||||
|
@ -2144,11 +2146,9 @@ CreateIndexHelper::DoDatabaseWork(mozIStorageConnection* aConnection)
|
|||
nsresult
|
||||
CreateIndexHelper::InsertDataFromObjectStore(mozIStorageConnection* aConnection)
|
||||
{
|
||||
bool autoIncrement = mIndex->IsAutoIncrement();
|
||||
|
||||
nsCAutoString table;
|
||||
nsCAutoString columns;
|
||||
if (autoIncrement) {
|
||||
if (mIndex->IsAutoIncrement()) {
|
||||
table.AssignLiteral("ai_object_data");
|
||||
columns.AssignLiteral("id, data");
|
||||
}
|
||||
|
@ -2173,27 +2173,40 @@ CreateIndexHelper::InsertDataFromObjectStore(mozIStorageConnection* aConnection)
|
|||
PRBool hasResult;
|
||||
while (NS_SUCCEEDED(stmt->ExecuteStep(&hasResult)) && hasResult) {
|
||||
nsCOMPtr<mozIStorageStatement> insertStmt =
|
||||
mTransaction->IndexUpdateStatement(autoIncrement, mIndex->IsUnique(),
|
||||
false);
|
||||
mTransaction->IndexUpdateStatement(mIndex->IsAutoIncrement(),
|
||||
mIndex->IsUnique(), false);
|
||||
NS_ENSURE_TRUE(insertStmt, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
|
||||
|
||||
mozStorageStatementScoper scoper2(insertStmt);
|
||||
|
||||
rv = insertStmt->BindInt64ByName(NS_LITERAL_CSTRING("index_id"), mId);
|
||||
rv = insertStmt->BindInt64ByName(NS_LITERAL_CSTRING("index_id"),
|
||||
mIndex->Id());
|
||||
NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
|
||||
|
||||
rv = insertStmt->BindInt64ByName(NS_LITERAL_CSTRING("object_data_id"),
|
||||
stmt->AsInt64(0));
|
||||
NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
|
||||
|
||||
if (!autoIncrement) {
|
||||
// XXX does this cause problems with the affinity?
|
||||
nsString key;
|
||||
rv = stmt->GetString(2, key);
|
||||
if (!mIndex->IsAutoIncrement()) {
|
||||
NS_NAMED_LITERAL_CSTRING(objectDataKey, "object_data_key");
|
||||
|
||||
PRInt32 keyType;
|
||||
rv = stmt->GetTypeOfIndex(2, &keyType);
|
||||
NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
|
||||
|
||||
rv = insertStmt->BindStringByName(NS_LITERAL_CSTRING("object_data_key"),
|
||||
key);
|
||||
if (keyType == mozIStorageStatement::VALUE_TYPE_INTEGER) {
|
||||
rv = insertStmt->BindInt64ByName(objectDataKey, stmt->AsInt64(2));
|
||||
}
|
||||
else if (keyType == mozIStorageStatement::VALUE_TYPE_TEXT) {
|
||||
nsString stringKey;
|
||||
rv = stmt->GetString(2, stringKey);
|
||||
NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
|
||||
|
||||
rv = insertStmt->BindStringByName(objectDataKey, stringKey);
|
||||
}
|
||||
else {
|
||||
NS_NOTREACHED("Bad SQLite type!");
|
||||
}
|
||||
NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
|
||||
}
|
||||
|
||||
|
@ -2209,12 +2222,11 @@ CreateIndexHelper::InsertDataFromObjectStore(mozIStorageConnection* aConnection)
|
|||
&cx, key);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
NS_NAMED_LITERAL_CSTRING(value, "value");
|
||||
|
||||
if (key.IsUnset()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
NS_NAMED_LITERAL_CSTRING(value, "value");
|
||||
if (key.IsInt()) {
|
||||
rv = insertStmt->BindInt64ByName(value, key.IntValue());
|
||||
}
|
||||
|
|
|
@ -60,10 +60,9 @@ interface nsIIDBTransaction : nsISupports
|
|||
const unsigned short DONE = 2;
|
||||
readonly attribute unsigned short readyState;
|
||||
|
||||
const unsigned short READ_WRITE = 0;
|
||||
const unsigned short READ_ONLY = 1;
|
||||
const unsigned short SNAPSHOT_READ = 2;
|
||||
const unsigned short VERSION_CHANGE = 3;
|
||||
const unsigned short READ_ONLY = 0;
|
||||
const unsigned short READ_WRITE = 1;
|
||||
const unsigned short VERSION_CHANGE = 2;
|
||||
readonly attribute unsigned short mode;
|
||||
|
||||
readonly attribute nsIDOMDOMStringList objectStoreNames;
|
||||
|
|
|
@ -56,6 +56,7 @@ TEST_FILES = \
|
|||
test_bfcache.html \
|
||||
test_clear.html \
|
||||
test_create_index.html \
|
||||
test_create_index_with_integer_keys.html \
|
||||
test_create_objectStore.html \
|
||||
test_cursors.html \
|
||||
test_cursor_mutation.html \
|
||||
|
|
|
@ -0,0 +1,76 @@
|
|||
<!--
|
||||
Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/
|
||||
-->
|
||||
<html>
|
||||
<head>
|
||||
<title>Indexed Database Test</title>
|
||||
|
||||
<script type="text/javascript" src="/MochiKit/packed.js"></script>
|
||||
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
|
||||
|
||||
<script type="text/javascript;version=1.7">
|
||||
function testSteps()
|
||||
{
|
||||
const data = { id: new Date().getTime(),
|
||||
num: parseInt(Math.random() * 1000) };
|
||||
|
||||
let request = mozIndexedDB.open(window.location.pathname);
|
||||
request.onerror = errorHandler;
|
||||
request.onsuccess = grabEventAndContinueHandler;
|
||||
let event = yield;
|
||||
|
||||
let db = event.target.result;
|
||||
db.onerror = errorHandler;
|
||||
|
||||
db.setVersion("1").onsuccess = grabEventAndContinueHandler;
|
||||
event = yield;
|
||||
|
||||
event.target.transaction.oncomplete = continueToNextStep;
|
||||
|
||||
// Make object store, add data.
|
||||
let objectStore = db.createObjectStore("foo", { keyPath: "id" });
|
||||
objectStore.add(data);
|
||||
yield;
|
||||
|
||||
db.setVersion("2").onsuccess = grabEventAndContinueHandler;
|
||||
event = yield;
|
||||
|
||||
event.target.transaction.oncomplete = continueToNextStep;
|
||||
|
||||
// Create index.
|
||||
event.target.transaction.objectStore("foo").createIndex("foo", "num");
|
||||
yield;
|
||||
|
||||
// Make sure our object made it into the index.
|
||||
let seenCount = 0;
|
||||
|
||||
|
||||
db.transaction("foo").objectStore("foo").index("foo")
|
||||
.openKeyCursor().onsuccess = function(event) {
|
||||
let cursor = event.target.result;
|
||||
if (cursor) {
|
||||
is(cursor.key, data.num, "Good key");
|
||||
is(cursor.value, data.id, "Good value");
|
||||
seenCount++;
|
||||
cursor.continue();
|
||||
}
|
||||
else {
|
||||
continueToNextStep();
|
||||
}
|
||||
};
|
||||
yield;
|
||||
|
||||
is(seenCount, 1, "Saw our entry");
|
||||
|
||||
finishTest();
|
||||
yield;
|
||||
}
|
||||
</script>
|
||||
<script type="text/javascript;version=1.7" src="helpers.js"></script>
|
||||
</head>
|
||||
|
||||
<body onload="runTest();"></body>
|
||||
|
||||
</html>
|
|
@ -803,6 +803,39 @@ function startTest()
|
|||
expectFocusShift(function () synthesizeKey("VK_TAB", { shiftKey: true }),
|
||||
null, textbox1.inputField, true, "shift+tab on textbox with tabindex set");
|
||||
|
||||
// ---- test for bug 618907 which ensures that canceling the mousedown event still focuses the
|
||||
// right frame
|
||||
|
||||
var childContentFrame = document.getElementById("ifa")
|
||||
childContentFrame.style.MozUserFocus = "";
|
||||
|
||||
var frab = childContentFrame.contentDocument.getElementById("fra-b");
|
||||
var mouseDownListener = function(event) event.preventDefault();
|
||||
frab.addEventListener("mousedown", mouseDownListener, false);
|
||||
|
||||
var childElementToFocus = childContentFrame.contentDocument.getElementById("fra");
|
||||
gLastFocus = childElementToFocus;
|
||||
gLastFocusWindow = childContentFrame.contentWindow;
|
||||
gLastFocus.focus();
|
||||
gEvents = "";
|
||||
|
||||
setFocusTo("t1", window);
|
||||
|
||||
gLastFocusMethod = -1;
|
||||
expectFocusShift(function () synthesizeMouse(frab, 5, 5, { }, childContentFrame.contentWindow),
|
||||
null, childElementToFocus, true,
|
||||
"mousedown event canceled - chrome to content");
|
||||
|
||||
frab.removeEventListener("mousedown", mouseDownListener, false);
|
||||
|
||||
var t5 = getById("t5");
|
||||
t5.addEventListener("mousedown", mouseDownListener, false);
|
||||
synthesizeMouse(t5, 10, 10, { })
|
||||
t5.removeEventListener("mousedown", mouseDownListener, false);
|
||||
is(fm.focusedElement, childElementToFocus,
|
||||
"mousedown event cancelled - content to chrome - element");
|
||||
is(fm.focusedWindow, childContentFrame.contentWindow, "mousedown event cancelled - content to chrome - window");
|
||||
|
||||
// ---- test to check that refocusing an element during a blur event doesn't succeed
|
||||
|
||||
var t1 = getById("t1");
|
||||
|
@ -1667,8 +1700,8 @@ SimpleTest.waitForFocus(startTest);
|
|||
<checkbox id="n2"/><checkbox id="n4"/><checkbox id="n6"/><checkbox id="n8"/><checkbox id="n10"/><checkbox id="n12"/>
|
||||
<listbox id="last" width="20" rows="1"/>
|
||||
|
||||
<iframe id="ifa" width="20" height="20" style="-moz-user-focus: ignore;"
|
||||
src="data:text/html,<input id=fra>
|
||||
<iframe id="ifa" width="40" height="60" style="-moz-user-focus: ignore;" type="content"
|
||||
src="data:text/html,<input id=fra size='2'><input id='fra-b' size='2'>
|
||||
<iframe src='data:text/html,<input id=frc><iframe src="data:text/html,<input id=frd>"></iframe>'></iframe>"/>
|
||||
<iframe id="ifb" width="20" height="20" style="-moz-user-focus: ignore;"
|
||||
src="data:text/html,<input id=frd></iframe>"/>
|
||||
|
|
|
@ -47,7 +47,7 @@ public class GeckoConnectivityReceiver
|
|||
public void onReceive(Context context, Intent intent) {
|
||||
String status;
|
||||
ConnectivityManager cm = (ConnectivityManager)
|
||||
GeckoApp.mAppContext.getSystemService(Context.CONNECTIVITY_SERVICE);
|
||||
context.getSystemService(Context.CONNECTIVITY_SERVICE);
|
||||
NetworkInfo info = cm.getActiveNetworkInfo();
|
||||
if (info == null)
|
||||
status = "unknown";
|
||||
|
|
|
@ -51,6 +51,7 @@ XPCSHELL_TESTS = unit
|
|||
_TEST_FILES = $(addprefix mochitest/, \
|
||||
test_bug509244.html \
|
||||
test_bug513439.html \
|
||||
test_acceleration.html \
|
||||
)
|
||||
|
||||
ifndef MOZ_ENABLE_LIBXUL
|
||||
|
|
|
@ -0,0 +1,71 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<!--
|
||||
https://bugzilla.mozilla.org/show_bug.cgi?id=627498
|
||||
-->
|
||||
<head>
|
||||
<title>Test hardware acceleration</title>
|
||||
<script type="application/javascript" src="/MochiKit/packed.js"></script>
|
||||
<script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
|
||||
</head>
|
||||
<body>
|
||||
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=627498">Mozilla Bug 627498</a>
|
||||
<p id="display"></p>
|
||||
<div id="content" style="display: none">
|
||||
|
||||
</div>
|
||||
<pre id="test">
|
||||
<script type="application/javascript">
|
||||
|
||||
// Make sure that acceleration is enabled/disabled the way we expect it to be
|
||||
// based on platform.
|
||||
|
||||
netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
|
||||
|
||||
Components.utils.import("resource://gre/modules/Services.jsm");
|
||||
|
||||
var Cc = Components.classes;
|
||||
var Ci = Components.interfaces;
|
||||
|
||||
var sysInfo = Cc["@mozilla.org/system-info;1"].getService(Ci.nsIPropertyBag2);
|
||||
|
||||
var windows = Services.ww.getWindowEnumerator();
|
||||
var acceleratedWindows = 0;
|
||||
while (windows.hasMoreElements()) {
|
||||
awindow = windows.getNext().QueryInterface(Ci.nsIInterfaceRequestor);
|
||||
windowutils = awindow.getInterface(Ci.nsIDOMWindowUtils);
|
||||
if (windowutils.layerManagerType != "Basic") {
|
||||
acceleratedWindows++;
|
||||
}
|
||||
}
|
||||
|
||||
switch(sysInfo.getProperty("name"))
|
||||
{
|
||||
case "Windows_NT":
|
||||
ok(acceleratedWindows != 0, "Acceleration enabled on Windows");
|
||||
|
||||
// Also check that D2D is enabled.
|
||||
var version = parseFloat(sysInfo.getProperty("version"));
|
||||
if (version >= 6.0) {
|
||||
var gfxInfo = Cc["@mozilla.org/gfx/info;1"].getService(Ci.nsIGfxInfo);
|
||||
ok(gfxInfo.D2DEnabled, "D2D enabled on Windows Vista or higher");
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case "Darwin":
|
||||
// We only enable OpenGL layers on machines that don't support QuickDraw
|
||||
// plugins. x86-64 architecture is a good proxy for this plugin support.
|
||||
if (sysInfo.getProperty("arch") == "x86-64") {
|
||||
ok(acceleratedWindows != 0, "Acceleration enabled on x86-64 OS X");
|
||||
} else {
|
||||
ok(acceleratedWindows == 0, "Acceleration not enabled on x86 OS X");
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
</script>
|
||||
</pre>
|
||||
</body>
|
||||
</html>
|
|
@ -43,6 +43,9 @@
|
|||
namespace mozilla {
|
||||
namespace SSE2 {
|
||||
|
||||
#if defined(__i386__) && defined(__MINGW32__)
|
||||
__attribute__((__force_align_arg_pointer__))
|
||||
#endif
|
||||
void
|
||||
Convert_ascii_run(const char *&src,
|
||||
PRUnichar *&dst,
|
||||
|
|
|
@ -556,6 +556,9 @@ TAG_PROGRAM = xargs etags -a
|
|||
ifneq ($(CPPSRCS)$(CMMSRCS),)
|
||||
CPP_PROG_LINK = 1
|
||||
endif
|
||||
ifneq ($(HOST_CPPSRCS)$(HOST_CMMSRCS),)
|
||||
HOST_CPP_PROG_LINK = 1
|
||||
endif
|
||||
|
||||
#
|
||||
# Make sure to wrap static libs inside linker specific flags to turn on & off
|
||||
|
@ -1085,11 +1088,11 @@ ifdef MSMANIFEST_TOOL
|
|||
fi
|
||||
endif # MSVC with manifest tool
|
||||
else
|
||||
ifeq ($(CPP_PROG_LINK),1)
|
||||
ifeq ($(HOST_CPP_PROG_LINK),1)
|
||||
$(HOST_CXX) -o $@ $(HOST_CXXFLAGS) $(HOST_LDFLAGS) $(HOST_PROGOBJS) $(HOST_LIBS) $(HOST_EXTRA_LIBS)
|
||||
else
|
||||
$(HOST_CC) -o $@ $(HOST_CFLAGS) $(HOST_LDFLAGS) $(HOST_PROGOBJS) $(HOST_LIBS) $(HOST_EXTRA_LIBS)
|
||||
endif # CPP_PROG_LINK
|
||||
endif # HOST_CPP_PROG_LINK
|
||||
endif
|
||||
endif
|
||||
|
||||
|
|
|
@ -1509,6 +1509,101 @@ MoveableWrapperFinder(JSDHashTable *table, JSDHashEntryHdr *hdr,
|
|||
return JS_DHASH_NEXT;
|
||||
}
|
||||
|
||||
static nsresult
|
||||
MoveWrapper(XPCCallContext& ccx, XPCWrappedNative *wrapper,
|
||||
XPCWrappedNativeScope *newScope, XPCWrappedNativeScope *oldScope)
|
||||
{
|
||||
// First, check to see if this wrapper really needs to be
|
||||
// reparented.
|
||||
|
||||
if (wrapper->GetScope() == newScope)
|
||||
{
|
||||
// The wrapper already got moved, nothing to do here.
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsISupports *identity = wrapper->GetIdentityObject();
|
||||
nsCOMPtr<nsIClassInfo> info(do_QueryInterface(identity));
|
||||
|
||||
// ClassInfo is implemented as singleton objects. If the identity
|
||||
// object here is the same object as returned by the QI, then it
|
||||
// is the singleton classinfo, so we don't need to reparent it.
|
||||
if(SameCOMIdentity(identity, info))
|
||||
info = nsnull;
|
||||
|
||||
if(!info)
|
||||
return NS_OK;
|
||||
|
||||
XPCNativeScriptableCreateInfo sciProto;
|
||||
XPCNativeScriptableCreateInfo sci;
|
||||
const XPCNativeScriptableCreateInfo& sciWrapper =
|
||||
XPCWrappedNative::GatherScriptableCreateInfo(identity, info,
|
||||
sciProto, sci);
|
||||
|
||||
// If the wrapper doesn't want precreate, then we don't need to
|
||||
// worry about reparenting it.
|
||||
if(!sciWrapper.GetFlags().WantPreCreate())
|
||||
return NS_OK;
|
||||
|
||||
JSObject *newParent = oldScope->GetGlobalJSObject();
|
||||
nsresult rv = sciWrapper.GetCallback()->PreCreate(identity, ccx,
|
||||
newParent,
|
||||
&newParent);
|
||||
if(NS_FAILED(rv))
|
||||
return rv;
|
||||
|
||||
if(newParent == oldScope->GetGlobalJSObject())
|
||||
{
|
||||
// The old scope still works for this wrapper. We have to
|
||||
// assume that the wrapper will continue to return the old
|
||||
// scope from PreCreate, so don't move it.
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
// The wrapper returned a new parent. If the new parent is in a
|
||||
// different scope, then we need to reparent it, otherwise, the
|
||||
// old scope is fine.
|
||||
|
||||
XPCWrappedNativeScope *betterScope =
|
||||
XPCWrappedNativeScope::FindInJSObjectScope(ccx, newParent);
|
||||
if(betterScope == oldScope)
|
||||
{
|
||||
// The wrapper asked for a different object, but that object
|
||||
// was in the same scope. This means that the new parent
|
||||
// simply hasn't been reparented yet, so reparent it first,
|
||||
// and then continue reparenting the wrapper itself.
|
||||
|
||||
if (!IS_WN_WRAPPER_OBJECT(newParent)) {
|
||||
// The parent of wrapper is a slim wrapper, in this case
|
||||
// we need to morph the parent so that we can reparent it.
|
||||
|
||||
rv = MorphSlimWrapper(ccx, newParent);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
}
|
||||
|
||||
XPCWrappedNative *parentWrapper =
|
||||
XPCWrappedNative::GetWrappedNativeOfJSObject(ccx, newParent);
|
||||
|
||||
rv = MoveWrapper(ccx, parentWrapper, newScope, oldScope);
|
||||
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
newParent = parentWrapper->GetFlatJSObjectNoMark();
|
||||
}
|
||||
else
|
||||
NS_ASSERTION(betterScope == newScope, "Weird scope returned");
|
||||
|
||||
// Now, reparent the wrapper, since we know that it wants to be
|
||||
// reparented.
|
||||
|
||||
nsRefPtr<XPCWrappedNative> junk;
|
||||
rv = XPCWrappedNative::ReparentWrapperIfFound(ccx, oldScope,
|
||||
newScope, newParent,
|
||||
wrapper->GetIdentityObject(),
|
||||
getter_AddRefs(junk));
|
||||
return rv;
|
||||
}
|
||||
|
||||
/* void moveWrappers(in JSContextPtr aJSContext, in JSObjectPtr aOldScope, in JSObjectPtr aNewScope); */
|
||||
NS_IMETHODIMP
|
||||
nsXPConnect::MoveWrappers(JSContext *aJSContext,
|
||||
|
@ -1543,72 +1638,7 @@ nsXPConnect::MoveWrappers(JSContext *aJSContext,
|
|||
// Now that we have the wrappers, reparent them to the new scope.
|
||||
for(PRUint32 i = 0, stop = wrappersToMove.Length(); i < stop; ++i)
|
||||
{
|
||||
// First, check to see if this wrapper really needs to be
|
||||
// reparented.
|
||||
|
||||
XPCWrappedNative *wrapper = wrappersToMove[i];
|
||||
nsISupports *identity = wrapper->GetIdentityObject();
|
||||
nsCOMPtr<nsIClassInfo> info(do_QueryInterface(identity));
|
||||
|
||||
// ClassInfo is implemented as singleton objects. If the identity
|
||||
// object here is the same object as returned by the QI, then it
|
||||
// is the singleton classinfo, so we don't need to reparent it.
|
||||
if(SameCOMIdentity(identity, info))
|
||||
info = nsnull;
|
||||
|
||||
if(!info)
|
||||
continue;
|
||||
|
||||
XPCNativeScriptableCreateInfo sciProto;
|
||||
XPCNativeScriptableCreateInfo sci;
|
||||
const XPCNativeScriptableCreateInfo& sciWrapper =
|
||||
XPCWrappedNative::GatherScriptableCreateInfo(identity, info,
|
||||
sciProto, sci);
|
||||
|
||||
// If the wrapper doesn't want precreate, then we don't need to
|
||||
// worry about reparenting it.
|
||||
if(!sciWrapper.GetFlags().WantPreCreate())
|
||||
continue;
|
||||
|
||||
JSObject *newParent = aOldScope;
|
||||
nsresult rv = sciWrapper.GetCallback()->PreCreate(identity, ccx,
|
||||
aOldScope,
|
||||
&newParent);
|
||||
if(NS_FAILED(rv))
|
||||
return rv;
|
||||
|
||||
if(newParent == aOldScope)
|
||||
{
|
||||
// The old scope still works for this wrapper. We have to assume
|
||||
// that the wrapper will continue to return the old scope from
|
||||
// PreCreate, so don't move it.
|
||||
continue;
|
||||
}
|
||||
|
||||
// The wrapper returned a new parent. If the new parent is in
|
||||
// a different scope, then we need to reparent it, otherwise,
|
||||
// the old scope is fine.
|
||||
|
||||
XPCWrappedNativeScope *betterScope =
|
||||
XPCWrappedNativeScope::FindInJSObjectScope(ccx, newParent);
|
||||
if(betterScope == oldScope)
|
||||
{
|
||||
// The wrapper asked for a different object, but that object
|
||||
// was in the same scope. We assume here that the new parent
|
||||
// simply hasn't been reparented yet.
|
||||
newParent = nsnull;
|
||||
}
|
||||
else
|
||||
NS_ASSERTION(betterScope == newScope, "Weird scope returned");
|
||||
|
||||
// Now, reparent the wrapper, since we know that it wants to be
|
||||
// reparented.
|
||||
|
||||
nsRefPtr<XPCWrappedNative> junk;
|
||||
rv = XPCWrappedNative::ReparentWrapperIfFound(ccx, oldScope,
|
||||
newScope, newParent,
|
||||
wrapper->GetIdentityObject(),
|
||||
getter_AddRefs(junk));
|
||||
nsresult rv = MoveWrapper(ccx, wrappersToMove[i], newScope, oldScope);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
}
|
||||
|
||||
|
|
|
@ -779,12 +779,8 @@ void DEBUG_CheckForComponentsInScope(JSContext* cx, JSObject* obj,
|
|||
// indicates a problem that should be addressed in the design and use of the
|
||||
// callback code.
|
||||
NS_ERROR("XPConnect is being called on a scope without a 'Components' property! (stack and details follow)");
|
||||
// Dumping the JS stack causes fatal JS asserts in some cases, so
|
||||
// comment it out for now.
|
||||
#if 0
|
||||
printf("The current JS stack is:\n");
|
||||
xpc_DumpJSStack(cx, JS_TRUE, JS_TRUE, JS_TRUE);
|
||||
#endif
|
||||
|
||||
printf("And the object whose scope lacks a 'Components' property is:\n");
|
||||
js_DumpObject(startingObj);
|
||||
|
|
|
@ -53,24 +53,37 @@ Features to add:
|
|||
-->
|
||||
<html lang="en-US" xml:lang="en-US" xmlns="http://www.w3.org/1999/xhtml">
|
||||
<head>
|
||||
<title>Reftest analyzer</title>
|
||||
<style type="text/css"><![CDATA[
|
||||
<title>Reftest analyzer</title>
|
||||
<style type="text/css"><![CDATA[
|
||||
|
||||
#itemlist { width: 300px; }
|
||||
#images { position: fixed; top: 10px; left: 310px; }
|
||||
#leftpane { width: 320px; }
|
||||
#images { position: fixed; top: 10px; left: 340px; }
|
||||
|
||||
form#imgcontrols { margin: 0; display: block; }
|
||||
form#imgcontrols { margin: 0; display: block; }
|
||||
|
||||
#itemlist > table { border-collapse: collapse; }
|
||||
#itemlist > table > tbody > tr > td { border: 1px solid; padding: 1px; }
|
||||
#itemlist > table { border-collapse: collapse; }
|
||||
#itemlist > table > tbody > tr > td { border: 1px solid; padding: 1px; }
|
||||
|
||||
/*
|
||||
#itemlist > table > tbody > tr.pass > td.url { background: lime; }
|
||||
#itemlist > table > tbody > tr.fail > td.url { background: red; }
|
||||
*/
|
||||
/*
|
||||
#itemlist > table > tbody > tr.pass > td.url { background: lime; }
|
||||
#itemlist > table > tbody > tr.fail > td.url { background: red; }
|
||||
*/
|
||||
|
||||
]]></style>
|
||||
<script type="text/javascript"><![CDATA[
|
||||
#magnification { margin-top: 1em; }
|
||||
#pixelinfo { font: small sans-serif; position: absolute; width: 200px; left: 100px; margin-top: 2px; }
|
||||
#pixelinfo table { border-collapse: collapse; white-space: nowrap;}
|
||||
#pixelinfo table td, #pixelinfo table th { padding: 0 6px 2px 0; text-align: left; }
|
||||
#pixelinfo table td { font-family: monospace; }
|
||||
|
||||
#pixelhint { display: inline; color: #88f; cursor: help; }
|
||||
#pixelhint > * { display: none; position: absolute; margin: 8px 0 0 8px; padding: 4px; width: 400px; background: #ffa; color: black; box-shadow: 3px 3px 2px #888; z-index: 1; }
|
||||
#pixelhint:hover { color: #000; }
|
||||
#pixelhint:hover > * { display: block; }
|
||||
#pixelhint p { margin: 0; }
|
||||
#pixelhint p + p { margin-top: 1em; }
|
||||
|
||||
]]></style>
|
||||
<script type="text/javascript"><![CDATA[
|
||||
|
||||
var XLINK_NS = "http://www.w3.org/1999/xlink";
|
||||
var SVG_NS = "http://www.w3.org/2000/svg";
|
||||
|
@ -79,193 +92,344 @@ var gPhases = null;
|
|||
|
||||
var gIDCache = {};
|
||||
|
||||
var gMagPixPaths = []; // 2D array of array-of-two <path> objects used in the pixel magnifier
|
||||
var gMagWidth = 5; // number of zoomed in pixels to show horizontally
|
||||
var gMagHeight = 5; // number of zoomed in pixels to show vertically
|
||||
var gMagZoom = 16; // size of the zoomed in pixels
|
||||
var gImage1Data; // ImageData object for the reference image
|
||||
var gImage2Data; // ImageData object for the test output image
|
||||
var gFlashingPixels = []; // array of <path> objects that should be flashed due to pixel color mismatch
|
||||
|
||||
function ID(id) {
|
||||
if (!(id in gIDCache))
|
||||
gIDCache[id] = document.getElementById(id);
|
||||
return gIDCache[id];
|
||||
if (!(id in gIDCache))
|
||||
gIDCache[id] = document.getElementById(id);
|
||||
return gIDCache[id];
|
||||
}
|
||||
|
||||
function load() {
|
||||
gPhases = [ ID("entry"), ID("loading"), ID("viewer") ];
|
||||
gPhases = [ ID("entry"), ID("loading"), ID("viewer") ];
|
||||
build_mag();
|
||||
}
|
||||
|
||||
function build_mag() {
|
||||
var mag = ID("mag");
|
||||
|
||||
var r = document.createElementNS(SVG_NS, "rect");
|
||||
r.setAttribute("x", gMagZoom * -gMagWidth / 2);
|
||||
r.setAttribute("y", gMagZoom * -gMagHeight / 2);
|
||||
r.setAttribute("width", gMagZoom * gMagWidth);
|
||||
r.setAttribute("height", gMagZoom * gMagHeight);
|
||||
mag.appendChild(r);
|
||||
|
||||
mag.setAttribute("transform", "translate(" + (gMagZoom * (gMagWidth / 2) + 1) + "," + (gMagZoom * (gMagHeight / 2) + 1) + ")");
|
||||
|
||||
for (var x = 0; x < gMagWidth; x++) {
|
||||
gMagPixPaths[x] = [];
|
||||
for (var y = 0; y < gMagHeight; y++) {
|
||||
var p1 = document.createElementNS(SVG_NS, "path");
|
||||
p1.setAttribute("d", "M" + ((x - gMagWidth / 2) + 1) * gMagZoom + "," + (y - gMagHeight / 2) * gMagZoom + "h" + -gMagZoom + "v" + gMagZoom);
|
||||
p1.setAttribute("stroke", "black");
|
||||
p1.setAttribute("stroke-width", "1px");
|
||||
p1.setAttribute("fill", "#aaa");
|
||||
|
||||
var p2 = document.createElementNS(SVG_NS, "path");
|
||||
p2.setAttribute("d", "M" + ((x - gMagWidth / 2) + 1) * gMagZoom + "," + (y - gMagHeight / 2) * gMagZoom + "v" + gMagZoom + "h" + -gMagZoom);
|
||||
p2.setAttribute("stroke", "black");
|
||||
p2.setAttribute("stroke-width", "1px");
|
||||
p2.setAttribute("fill", "#888");
|
||||
|
||||
mag.appendChild(p1);
|
||||
mag.appendChild(p2);
|
||||
gMagPixPaths[x][y] = [p1, p2];
|
||||
}
|
||||
}
|
||||
|
||||
var flashedOn = false;
|
||||
setInterval(function() {
|
||||
flashedOn = !flashedOn;
|
||||
flash_pixels(flashedOn);
|
||||
}, 500);
|
||||
}
|
||||
|
||||
function show_phase(phaseid) {
|
||||
for (var i in gPhases) {
|
||||
var phase = gPhases[i];
|
||||
phase.style.display = (phase.id == phaseid) ? "" : "none";
|
||||
}
|
||||
for (var i in gPhases) {
|
||||
var phase = gPhases[i];
|
||||
phase.style.display = (phase.id == phaseid) ? "" : "none";
|
||||
}
|
||||
|
||||
if (phase == "viewer")
|
||||
ID("images").style.display = "none";
|
||||
if (phase == "viewer")
|
||||
ID("images").style.display = "none";
|
||||
}
|
||||
|
||||
function fileentry_changed() {
|
||||
show_phase("loading");
|
||||
var input = ID("fileentry");
|
||||
var files = input.files;
|
||||
var log = null;
|
||||
if (files.length > 0) {
|
||||
// Only handle the first file; don't handle multiple selection.
|
||||
// The parts of the log we care about are ASCII-only. Since we
|
||||
// can ignore lines we don't care about, best to read in as
|
||||
// iso-8859-1, which guarantees we don't get decoding errors.
|
||||
log = files[0].getAsText("iso-8859-1");
|
||||
}
|
||||
// So the user can process the same filename again (after
|
||||
// overwriting the log), clear the value on the form input so we
|
||||
// will always get an onchange event.
|
||||
input.value = "";
|
||||
show_phase("loading");
|
||||
var input = ID("fileentry");
|
||||
var files = input.files;
|
||||
var log = null;
|
||||
if (files.length > 0) {
|
||||
// Only handle the first file; don't handle multiple selection.
|
||||
// The parts of the log we care about are ASCII-only. Since we
|
||||
// can ignore lines we don't care about, best to read in as
|
||||
// iso-8859-1, which guarantees we don't get decoding errors.
|
||||
log = files[0].getAsText("iso-8859-1");
|
||||
}
|
||||
// So the user can process the same filename again (after
|
||||
// overwriting the log), clear the value on the form input so we
|
||||
// will always get an onchange event.
|
||||
input.value = "";
|
||||
|
||||
if (log)
|
||||
process_log(log);
|
||||
else
|
||||
show_phase("entry");
|
||||
if (log)
|
||||
process_log(log);
|
||||
else
|
||||
show_phase("entry");
|
||||
}
|
||||
|
||||
function log_pasted() {
|
||||
show_phase("loading");
|
||||
var entry = ID("logentry");
|
||||
var log = entry.value;
|
||||
entry.value = "";
|
||||
process_log(log);
|
||||
show_phase("loading");
|
||||
var entry = ID("logentry");
|
||||
var log = entry.value;
|
||||
entry.value = "";
|
||||
process_log(log);
|
||||
}
|
||||
|
||||
var gTestItems;
|
||||
|
||||
function process_log(contents) {
|
||||
var lines = contents.split(/[\r\n]+/);
|
||||
gTestItems = [];
|
||||
for (var j in lines) {
|
||||
var line = lines[j];
|
||||
var match = line.match(/^(?:NEXT ERROR )?REFTEST (.*)$/);
|
||||
if (!match)
|
||||
continue;
|
||||
line = match[1];
|
||||
match = line.match(/^(TEST-PASS|TEST-UNEXPECTED-PASS|TEST-KNOWN-FAIL|TEST-UNEXPECTED-FAIL)(\(EXPECTED RANDOM\)|) \| ([^\|]+) \|(.*)/);
|
||||
if (match) {
|
||||
var state = match[1];
|
||||
var random = match[2];
|
||||
var url = match[3];
|
||||
var lines = contents.split(/[\r\n]+/);
|
||||
gTestItems = [];
|
||||
for (var j in lines) {
|
||||
var line = lines[j];
|
||||
var match = line.match(/^(?:NEXT ERROR )?REFTEST (.*)$/);
|
||||
if (!match)
|
||||
continue;
|
||||
line = match[1];
|
||||
match = line.match(/^(TEST-PASS|TEST-UNEXPECTED-PASS|TEST-KNOWN-FAIL|TEST-UNEXPECTED-FAIL)(\(EXPECTED RANDOM\)|) \| ([^\|]+) \|(.*)/);
|
||||
if (match) {
|
||||
var state = match[1];
|
||||
var random = match[2];
|
||||
var url = match[3];
|
||||
var extra = match[4];
|
||||
gTestItems.push(
|
||||
{
|
||||
pass: !state.match(/FAIL$/),
|
||||
// only one of the following three should ever be true
|
||||
unexpected: !!state.match(/^TEST-UNEXPECTED/),
|
||||
random: (random == "(EXPECTED RANDOM)"),
|
||||
skip: (extra == " (SKIP)"),
|
||||
url: url,
|
||||
images: []
|
||||
});
|
||||
continue;
|
||||
}
|
||||
match = line.match(/^ IMAGE[^:]*: (.*)$/);
|
||||
if (match) {
|
||||
var item = gTestItems[gTestItems.length - 1];
|
||||
item.images.push(match[1]);
|
||||
}
|
||||
}
|
||||
gTestItems.push(
|
||||
{
|
||||
pass: !state.match(/FAIL$/),
|
||||
// only one of the following three should ever be true
|
||||
unexpected: !!state.match(/^TEST-UNEXPECTED/),
|
||||
random: (random == "(EXPECTED RANDOM)"),
|
||||
skip: (extra == " (SKIP)"),
|
||||
url: url,
|
||||
images: []
|
||||
});
|
||||
continue;
|
||||
}
|
||||
match = line.match(/^ IMAGE[^:]*: (.*)$/);
|
||||
if (match) {
|
||||
var item = gTestItems[gTestItems.length - 1];
|
||||
item.images.push(match[1]);
|
||||
}
|
||||
}
|
||||
|
||||
build_viewer();
|
||||
build_viewer();
|
||||
}
|
||||
|
||||
function build_viewer() {
|
||||
if (gTestItems.length == 0) {
|
||||
show_phase("entry");
|
||||
return;
|
||||
}
|
||||
if (gTestItems.length == 0) {
|
||||
show_phase("entry");
|
||||
return;
|
||||
}
|
||||
|
||||
var cell = ID("itemlist");
|
||||
while (cell.childNodes.length > 0)
|
||||
cell.removeChild(cell.childNodes[cell.childNodes.length - 1]);
|
||||
var cell = ID("itemlist");
|
||||
while (cell.childNodes.length > 0)
|
||||
cell.removeChild(cell.childNodes[cell.childNodes.length - 1]);
|
||||
|
||||
var table = document.createElement("table");
|
||||
var tbody = document.createElement("tbody");
|
||||
table.appendChild(tbody);
|
||||
var table = document.createElement("table");
|
||||
var tbody = document.createElement("tbody");
|
||||
table.appendChild(tbody);
|
||||
|
||||
for (var i in gTestItems) {
|
||||
var item = gTestItems[i];
|
||||
for (var i in gTestItems) {
|
||||
var item = gTestItems[i];
|
||||
|
||||
// XXX skip expected pass items until we have filtering UI
|
||||
if (item.pass && !item.unexpected)
|
||||
continue;
|
||||
// XXX skip expected pass items until we have filtering UI
|
||||
if (item.pass && !item.unexpected)
|
||||
continue;
|
||||
|
||||
var tr = document.createElement("tr");
|
||||
var rowclass = item.pass ? "pass" : "fail";
|
||||
var td;
|
||||
var text;
|
||||
var tr = document.createElement("tr");
|
||||
var rowclass = item.pass ? "pass" : "fail";
|
||||
var td;
|
||||
var text;
|
||||
|
||||
td = document.createElement("td");
|
||||
text = "";
|
||||
if (item.unexpected) { text += "!"; rowclass += " unexpected"; }
|
||||
if (item.random) { text += "R"; rowclass += " random"; }
|
||||
if (item.skip) { text += "S"; rowclass += " skip"; }
|
||||
td.appendChild(document.createTextNode(text));
|
||||
tr.appendChild(td);
|
||||
td = document.createElement("td");
|
||||
text = "";
|
||||
if (item.unexpected) { text += "!"; rowclass += " unexpected"; }
|
||||
if (item.random) { text += "R"; rowclass += " random"; }
|
||||
if (item.skip) { text += "S"; rowclass += " skip"; }
|
||||
td.appendChild(document.createTextNode(text));
|
||||
tr.appendChild(td);
|
||||
|
||||
td = document.createElement("td");
|
||||
td.className = "url";
|
||||
// Only display part of URL after "/mozilla/".
|
||||
var match = item.url.match(/\/mozilla\/(.*)/);
|
||||
text = document.createTextNode(match ? match[1] : item.url);
|
||||
if (item.images.length > 0) {
|
||||
var a = document.createElement("a");
|
||||
a.href = "javascript:show_images(" + i + ")";
|
||||
a.appendChild(text);
|
||||
td.appendChild(a);
|
||||
} else {
|
||||
td.appendChild(text);
|
||||
}
|
||||
tr.appendChild(td);
|
||||
td = document.createElement("td");
|
||||
td.className = "url";
|
||||
// Only display part of URL after "/mozilla/".
|
||||
var match = item.url.match(/\/mozilla\/(.*)/);
|
||||
text = document.createTextNode(match ? match[1] : item.url);
|
||||
if (item.images.length > 0) {
|
||||
var a = document.createElement("a");
|
||||
a.href = "javascript:show_images(" + i + ")";
|
||||
a.appendChild(text);
|
||||
td.appendChild(a);
|
||||
} else {
|
||||
td.appendChild(text);
|
||||
}
|
||||
tr.appendChild(td);
|
||||
|
||||
tbody.appendChild(tr);
|
||||
}
|
||||
tbody.appendChild(tr);
|
||||
}
|
||||
|
||||
cell.appendChild(table);
|
||||
cell.appendChild(table);
|
||||
|
||||
show_phase("viewer");
|
||||
show_phase("viewer");
|
||||
}
|
||||
|
||||
function get_image_data(src, whenReady) {
|
||||
var img = new Image();
|
||||
img.onload = function() {
|
||||
var canvas = document.createElement("canvas");
|
||||
canvas.width = 800;
|
||||
canvas.height = 1000;
|
||||
|
||||
var ctx = canvas.getContext("2d");
|
||||
ctx.drawImage(img, 0, 0);
|
||||
|
||||
whenReady(ctx.getImageData(0, 0, 800, 1000));
|
||||
};
|
||||
img.src = src;
|
||||
}
|
||||
|
||||
function show_images(i) {
|
||||
var item = gTestItems[i];
|
||||
var cell = ID("images");
|
||||
var item = gTestItems[i];
|
||||
var cell = ID("images");
|
||||
|
||||
ID("image1").style.display = "";
|
||||
ID("image2").style.display = "none";
|
||||
ID("diffrect").style.display = "none";
|
||||
ID("imgcontrols").reset();
|
||||
ID("image1").style.display = "";
|
||||
ID("image2").style.display = "none";
|
||||
ID("diffrect").style.display = "none";
|
||||
ID("imgcontrols").reset();
|
||||
|
||||
ID("image1").setAttributeNS(XLINK_NS, "xlink:href", item.images[0]);
|
||||
// Making the href be #image1 doesn't seem to work
|
||||
ID("feimage1").setAttributeNS(XLINK_NS, "xlink:href", item.images[0]);
|
||||
if (item.images.length == 1) {
|
||||
ID("imgcontrols").style.display = "none";
|
||||
} else {
|
||||
ID("imgcontrols").style.display = "";
|
||||
ID("image1").setAttributeNS(XLINK_NS, "xlink:href", item.images[0]);
|
||||
// Making the href be #image1 doesn't seem to work
|
||||
ID("feimage1").setAttributeNS(XLINK_NS, "xlink:href", item.images[0]);
|
||||
if (item.images.length == 1) {
|
||||
ID("imgcontrols").style.display = "none";
|
||||
} else {
|
||||
ID("imgcontrols").style.display = "";
|
||||
|
||||
ID("image2").setAttributeNS(XLINK_NS, "xlink:href", item.images[1]);
|
||||
// Making the href be #image2 doesn't seem to work
|
||||
ID("feimage2").setAttributeNS(XLINK_NS, "xlink:href", item.images[1]);
|
||||
}
|
||||
ID("image2").setAttributeNS(XLINK_NS, "xlink:href", item.images[1]);
|
||||
// Making the href be #image2 doesn't seem to work
|
||||
ID("feimage2").setAttributeNS(XLINK_NS, "xlink:href", item.images[1]);
|
||||
}
|
||||
|
||||
cell.style.display = "";
|
||||
cell.style.display = "";
|
||||
|
||||
get_image_data(item.images[0], function(data) { gImage1Data = data });
|
||||
get_image_data(item.images[1], function(data) { gImage2Data = data });
|
||||
}
|
||||
|
||||
function show_image(i) {
|
||||
if (i == 1) {
|
||||
ID("image1").style.display = "";
|
||||
ID("image2").style.display = "none";
|
||||
} else {
|
||||
ID("image1").style.display = "none";
|
||||
ID("image2").style.display = "";
|
||||
}
|
||||
if (i == 1) {
|
||||
ID("image1").style.display = "";
|
||||
ID("image2").style.display = "none";
|
||||
} else {
|
||||
ID("image1").style.display = "none";
|
||||
ID("image2").style.display = "";
|
||||
}
|
||||
}
|
||||
|
||||
function show_differences(cb) {
|
||||
ID("diffrect").style.display = cb.checked ? "" : "none";
|
||||
ID("diffrect").style.display = cb.checked ? "" : "none";
|
||||
}
|
||||
|
||||
]]></script>
|
||||
function flash_pixels(on) {
|
||||
var stroke = on ? "red" : "black";
|
||||
var strokeWidth = on ? "2px" : "1px";
|
||||
for (var i = 0; i < gFlashingPixels.length; i++) {
|
||||
gFlashingPixels[i].setAttribute("stroke", stroke);
|
||||
gFlashingPixels[i].setAttribute("stroke-width", strokeWidth);
|
||||
}
|
||||
}
|
||||
|
||||
function cursor_point(evt) {
|
||||
var m = evt.target.getScreenCTM().inverse();
|
||||
var p = ID("svg").createSVGPoint();
|
||||
p.x = evt.clientX;
|
||||
p.y = evt.clientY;
|
||||
p = p.matrixTransform(m);
|
||||
return { x: Math.floor(p.x), y: Math.floor(p.y) };
|
||||
}
|
||||
|
||||
function hex2(i) {
|
||||
return (i < 16 ? "0" : "") + i.toString(16);
|
||||
}
|
||||
|
||||
function canvas_pixel_as_hex(data, x, y) {
|
||||
var offset = (y * data.width + x) * 4;
|
||||
var r = data.data[offset];
|
||||
var g = data.data[offset + 1];
|
||||
var b = data.data[offset + 2];
|
||||
return "#" + hex2(r) + hex2(g) + hex2(b);
|
||||
}
|
||||
|
||||
function hex_as_rgb(hex) {
|
||||
return "rgb(" + [parseInt(hex.substring(1, 3), 16), parseInt(hex.substring(3, 5), 16), parseInt(hex.substring(5, 7), 16)] + ")";
|
||||
}
|
||||
|
||||
function magnify(evt) {
|
||||
var { x: x, y: y } = cursor_point(evt);
|
||||
var centerPixelColor1, centerPixelColor2;
|
||||
|
||||
var dx_lo = -Math.floor(gMagWidth / 2);
|
||||
var dx_hi = Math.floor(gMagWidth / 2);
|
||||
var dy_lo = -Math.floor(gMagHeight / 2);
|
||||
var dy_hi = Math.floor(gMagHeight / 2);
|
||||
|
||||
flash_pixels(false);
|
||||
gFlashingPixels = [];
|
||||
for (var j = dy_lo; j <= dy_hi; j++) {
|
||||
for (var i = dx_lo; i <= dx_hi; i++) {
|
||||
var px = x + i;
|
||||
var py = y + j;
|
||||
var p1 = gMagPixPaths[i + dx_hi][j + dy_hi][0];
|
||||
var p2 = gMagPixPaths[i + dx_hi][j + dy_hi][1];
|
||||
if (px < 0 || py < 0 || px >= 800 || py >= 1000) {
|
||||
p1.setAttribute("fill", "#aaa");
|
||||
p2.setAttribute("fill", "#888");
|
||||
} else {
|
||||
var color1 = canvas_pixel_as_hex(gImage1Data, x + i, y + j);
|
||||
var color2 = canvas_pixel_as_hex(gImage2Data, x + i, y + j);
|
||||
p1.setAttribute("fill", color1);
|
||||
p2.setAttribute("fill", color2);
|
||||
if (color1 != color2) {
|
||||
gFlashingPixels.push(p1, p2);
|
||||
p1.parentNode.appendChild(p1);
|
||||
p2.parentNode.appendChild(p2);
|
||||
}
|
||||
if (i == 0 && j == 0) {
|
||||
centerPixelColor1 = color1;
|
||||
centerPixelColor2 = color2;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
flash_pixels(true);
|
||||
show_pixelinfo(x, y, centerPixelColor1, hex_as_rgb(centerPixelColor1), centerPixelColor2, hex_as_rgb(centerPixelColor2));
|
||||
}
|
||||
|
||||
function show_pixelinfo(x, y, pix1rgb, pix1hex, pix2rgb, pix2hex) {
|
||||
var pixelinfo = ID("pixelinfo");
|
||||
ID("coords").textContent = [x, y];
|
||||
ID("pix1hex").textContent = pix1hex;
|
||||
ID("pix1rgb").textContent = pix1rgb;
|
||||
ID("pix2hex").textContent = pix2hex;
|
||||
ID("pix2rgb").textContent = pix2rgb;
|
||||
}
|
||||
|
||||
]]></script>
|
||||
|
||||
</head>
|
||||
<body onload="load()">
|
||||
|
@ -275,7 +439,7 @@ function show_differences(cb) {
|
|||
<h1>Reftest analyzer: load reftest log</h1>
|
||||
|
||||
<p>Either paste your log into this textarea:<br />
|
||||
<textarea cols="80" rows="10" id="logentry" /><br/>
|
||||
<textarea cols="80" rows="10" id="logentry"/><br/>
|
||||
<input type="button" value="Process pasted log" onclick="log_pasted()" /></p>
|
||||
|
||||
<p>... or load it from a file:<br/>
|
||||
|
@ -286,7 +450,34 @@ function show_differences(cb) {
|
|||
<div id="loading" style="display:none">Loading log...</div>
|
||||
|
||||
<div id="viewer" style="display:none">
|
||||
<div id="itemlist"></div>
|
||||
<div id="leftpane">
|
||||
<div id="pixelinfo">
|
||||
<table>
|
||||
<tbody>
|
||||
<tr><th>Pixel at:</th><td colspan="2" id="coords"/></tr>
|
||||
<tr><th>Image 1:</th><td id="pix1rgb"></td><td id="pix1hex"></td></tr>
|
||||
<tr><th>Image 2:</th><td id="pix2rgb"></td><td id="pix2hex"></td></tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<div>
|
||||
<div id="pixelhint">★
|
||||
<div>
|
||||
<p>Move the mouse over the reftest image on the right to show
|
||||
magnified pixels on the left. The color information above is for
|
||||
the pixel centered in the magnified view.</p>
|
||||
<p>Image 1 is shown in the upper triangle of each pixel and Image 2
|
||||
is shown in the lower triangle.</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div id="magnification">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="100" height="100" shape-rendering="optimizeSpeed">
|
||||
<g id="mag"/>
|
||||
</svg>
|
||||
</div>
|
||||
<div id="itemlist"></div>
|
||||
</div>
|
||||
<div id="images" style="display:none">
|
||||
<form id="imgcontrols">
|
||||
<label><input type="radio" name="which" value="0" onchange="show_image(1)" checked="checked" />Image 1</label>
|
||||
|
@ -295,67 +486,69 @@ function show_differences(cb) {
|
|||
</form>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" width="800px" height="1000px" viewbox="0 0 800 1000" id="svg">
|
||||
<defs>
|
||||
<!-- use sRGB to avoid loss of data -->
|
||||
<filter id="showDifferences" x="0%" y="0%" width="100%" height="100%"
|
||||
style="color-interpolation-filters: sRGB">
|
||||
<feImage id="feimage1" result="img1" xlink:href="#image1" />
|
||||
<feImage id="feimage2" result="img2" xlink:href="#image2" />
|
||||
<!-- inv1 and inv2 are the images with RGB inverted -->
|
||||
<feComponentTransfer result="inv1" in="img1">
|
||||
<feFuncR type="linear" slope="-1" intercept="1" />
|
||||
<feFuncG type="linear" slope="-1" intercept="1" />
|
||||
<feFuncB type="linear" slope="-1" intercept="1" />
|
||||
</feComponentTransfer>
|
||||
<feComponentTransfer result="inv2" in="img2">
|
||||
<feFuncR type="linear" slope="-1" intercept="1" />
|
||||
<feFuncG type="linear" slope="-1" intercept="1" />
|
||||
<feFuncB type="linear" slope="-1" intercept="1" />
|
||||
</feComponentTransfer>
|
||||
<!-- w1 will have non-white pixels anywhere that img2
|
||||
is brighter than img1, and w2 for the reverse.
|
||||
It would be nice not to have to go through these
|
||||
intermediate states, but feComposite
|
||||
type="arithmetic" can't transform the RGB channels
|
||||
and leave the alpha channel untouched. -->
|
||||
<feComposite result="w1" in="img1" in2="inv2" operator="arithmetic" k2="1" k3="1" />
|
||||
<feComposite result="w2" in="img2" in2="inv1" operator="arithmetic" k2="1" k3="1" />
|
||||
<!-- c1 will have non-black pixels anywhere that img2
|
||||
is brighter than img1, and c2 for the reverse -->
|
||||
<feComponentTransfer result="c1" in="w1">
|
||||
<feFuncR type="linear" slope="-1" intercept="1" />
|
||||
<feFuncG type="linear" slope="-1" intercept="1" />
|
||||
<feFuncB type="linear" slope="-1" intercept="1" />
|
||||
</feComponentTransfer>
|
||||
<feComponentTransfer result="c2" in="w2">
|
||||
<feFuncR type="linear" slope="-1" intercept="1" />
|
||||
<feFuncG type="linear" slope="-1" intercept="1" />
|
||||
<feFuncB type="linear" slope="-1" intercept="1" />
|
||||
</feComponentTransfer>
|
||||
<!-- c will be nonblack (and fully on) for every pixel+component where there are differences -->
|
||||
<feComposite result="c" in="c1" in2="c2" operator="arithmetic" k2="255" k3="255" />
|
||||
<!-- a will be opaque for every pixel with differences and transparent for all others -->
|
||||
<feColorMatrix result="a" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 0 0" />
|
||||
|
||||
<!-- a, dilated by 4 pixels -->
|
||||
<feMorphology result="dila4" in="a" operator="dilate" radius="4" />
|
||||
<!-- a, dilated by 1 pixel -->
|
||||
<feMorphology result="dila1" in="a" operator="dilate" radius="1" />
|
||||
|
||||
<!-- all the pixels in the 3-pixel dilation of a but not in the 1-pixel dilation of a, to highlight the diffs -->
|
||||
<feComposite result="highlight" in="dila4" in2="dila1" operator="out" />
|
||||
<!-- use sRGB to avoid loss of data -->
|
||||
<filter id="showDifferences" x="0%" y="0%" width="100%" height="100%"
|
||||
style="color-interpolation-filters: sRGB">
|
||||
<feImage id="feimage1" result="img1" xlink:href="#image1" />
|
||||
<feImage id="feimage2" result="img2" xlink:href="#image2" />
|
||||
<!-- inv1 and inv2 are the images with RGB inverted -->
|
||||
<feComponentTransfer result="inv1" in="img1">
|
||||
<feFuncR type="linear" slope="-1" intercept="1" />
|
||||
<feFuncG type="linear" slope="-1" intercept="1" />
|
||||
<feFuncB type="linear" slope="-1" intercept="1" />
|
||||
</feComponentTransfer>
|
||||
<feComponentTransfer result="inv2" in="img2">
|
||||
<feFuncR type="linear" slope="-1" intercept="1" />
|
||||
<feFuncG type="linear" slope="-1" intercept="1" />
|
||||
<feFuncB type="linear" slope="-1" intercept="1" />
|
||||
</feComponentTransfer>
|
||||
<!-- w1 will have non-white pixels anywhere that img2
|
||||
is brighter than img1, and w2 for the reverse.
|
||||
It would be nice not to have to go through these
|
||||
intermediate states, but feComposite
|
||||
type="arithmetic" can't transform the RGB channels
|
||||
and leave the alpha channel untouched. -->
|
||||
<feComposite result="w1" in="img1" in2="inv2" operator="arithmetic" k2="1" k3="1" />
|
||||
<feComposite result="w2" in="img2" in2="inv1" operator="arithmetic" k2="1" k3="1" />
|
||||
<!-- c1 will have non-black pixels anywhere that img2
|
||||
is brighter than img1, and c2 for the reverse -->
|
||||
<feComponentTransfer result="c1" in="w1">
|
||||
<feFuncR type="linear" slope="-1" intercept="1" />
|
||||
<feFuncG type="linear" slope="-1" intercept="1" />
|
||||
<feFuncB type="linear" slope="-1" intercept="1" />
|
||||
</feComponentTransfer>
|
||||
<feComponentTransfer result="c2" in="w2">
|
||||
<feFuncR type="linear" slope="-1" intercept="1" />
|
||||
<feFuncG type="linear" slope="-1" intercept="1" />
|
||||
<feFuncB type="linear" slope="-1" intercept="1" />
|
||||
</feComponentTransfer>
|
||||
<!-- c will be nonblack (and fully on) for every pixel+component where there are differences -->
|
||||
<feComposite result="c" in="c1" in2="c2" operator="arithmetic" k2="255" k3="255" />
|
||||
<!-- a will be opaque for every pixel with differences and transparent for all others -->
|
||||
<feColorMatrix result="a" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 0 0" />
|
||||
|
||||
<!-- a, dilated by 4 pixels -->
|
||||
<feMorphology result="dila4" in="a" operator="dilate" radius="4" />
|
||||
<!-- a, dilated by 1 pixel -->
|
||||
<feMorphology result="dila1" in="a" operator="dilate" radius="1" />
|
||||
|
||||
<!-- all the pixels in the 3-pixel dilation of a but not in the 1-pixel dilation of a, to highlight the diffs -->
|
||||
<feComposite result="highlight" in="dila4" in2="dila1" operator="out" />
|
||||
|
||||
<feFlood result="red" flood-color="red" />
|
||||
<feComposite result="redhighlight" in="red" in2="highlight" operator="in" />
|
||||
<feFlood result="black" flood-color="black" flood-opacity="0.5" />
|
||||
<feMerge>
|
||||
<feMergeNode in="black" />
|
||||
<feMergeNode in="redhighlight" />
|
||||
</feMerge>
|
||||
</filter>
|
||||
<feFlood result="red" flood-color="red" />
|
||||
<feComposite result="redhighlight" in="red" in2="highlight" operator="in" />
|
||||
<feFlood result="black" flood-color="black" flood-opacity="0.5" />
|
||||
<feMerge>
|
||||
<feMergeNode in="black" />
|
||||
<feMergeNode in="redhighlight" />
|
||||
</feMerge>
|
||||
</filter>
|
||||
</defs>
|
||||
<image x="0" y="0" width="100%" height="100%" id="image1" />
|
||||
<image x="0" y="0" width="100%" height="100%" id="image2" />
|
||||
<rect id="diffrect" filter="url(#showDifferences)" x="0" y="0" width="100%" height="100%" />
|
||||
<g onmousemove="magnify(evt)">
|
||||
<image x="0" y="0" width="100%" height="100%" id="image1" />
|
||||
<image x="0" y="0" width="100%" height="100%" id="image2" />
|
||||
</g>
|
||||
<rect id="diffrect" filter="url(#showDifferences)" pointer-events="none" x="0" y="0" width="100%" height="100%" />
|
||||
</svg>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -57,7 +57,7 @@
|
|||
#include "nsILocalFile.h"
|
||||
#include "mozilla/FileUtils.h"
|
||||
|
||||
#if defined(XP_WIN)
|
||||
#if defined(XP_WIN) && defined(_MSC_VER)
|
||||
#define MOZ_WIN_MEM_TRY_BEGIN __try {
|
||||
#define MOZ_WIN_MEM_TRY_CATCH(cmd) } \
|
||||
__except(GetExceptionCode()==EXCEPTION_IN_PAGE_ERROR ? \
|
||||
|
|
|
@ -121,6 +121,9 @@ nsGIFDecoder2::nsGIFDecoder2()
|
|||
// Clear out the structure, excluding the arrays
|
||||
memset(&mGIFStruct, 0, sizeof(mGIFStruct));
|
||||
|
||||
// Initialize as "animate once" in case no NETSCAPE2.0 extension is found
|
||||
mGIFStruct.loop_count = 1;
|
||||
|
||||
// Start with the version (GIF89a|GIF87a)
|
||||
mGIFStruct.state = gif_type;
|
||||
mGIFStruct.bytes_to_consume = 6;
|
||||
|
|
|
@ -163,14 +163,14 @@ SVGDrawingCallback::operator()(gfxContext* aContext,
|
|||
}
|
||||
NS_ABORT_IF_FALSE(presShell, "GetPresShell succeeded but returned null");
|
||||
|
||||
aContext->Save();
|
||||
gfxContextAutoSaveRestore contextRestorer(aContext);
|
||||
|
||||
// Clip to aFillRect so that we don't paint outside.
|
||||
aContext->NewPath();
|
||||
aContext->Rectangle(aFillRect);
|
||||
aContext->Clip();
|
||||
gfxMatrix savedMatrix(aContext->CurrentMatrix());
|
||||
|
||||
gfxContextMatrixAutoSaveRestore contextMatrixRestorer(aContext);
|
||||
aContext->Multiply(gfxMatrix(aTransform).Invert());
|
||||
|
||||
|
||||
|
@ -191,9 +191,6 @@ SVGDrawingCallback::operator()(gfxContext* aContext,
|
|||
NS_RGBA(0, 0, 0, 0), // transparent
|
||||
aContext);
|
||||
|
||||
aContext->SetMatrix(savedMatrix);
|
||||
aContext->Restore();
|
||||
|
||||
return PR_TRUE;
|
||||
}
|
||||
|
||||
|
|
Двоичные данные
modules/plugin/test/testplugin/nptest.rc
Двоичные данные
modules/plugin/test/testplugin/nptest.rc
Двоичный файл не отображается.
|
@ -59,3 +59,16 @@ interface nsICacheInfoChannel : nsISupports
|
|||
*/
|
||||
boolean isFromCache();
|
||||
};
|
||||
|
||||
[scriptable, uuid(746064fc-8783-4d19-9c5d-6361ed807b39)]
|
||||
interface nsICacheInfoChannel_GECKO_2_0 : nsISupports
|
||||
{
|
||||
/**
|
||||
* Return an object that while not released prevents the channel from
|
||||
* releasing the cache entry after all work on it has been done. Used by
|
||||
* asynchronous consumers that needs to work with the cache entry long after
|
||||
* onStopRequest has been called. Must be acquired no later than during
|
||||
* onStopRequest.
|
||||
*/
|
||||
readonly attribute nsISupports cacheEntryClosePreventer;
|
||||
};
|
||||
|
|
|
@ -64,6 +64,7 @@ HttpBaseChannel::HttpBaseChannel()
|
|||
, mPriority(PRIORITY_NORMAL)
|
||||
, mCaps(0)
|
||||
, mRedirectionLimit(gHttpHandler->RedirectionLimit())
|
||||
, mCacheEntryClosePreventionCount(0)
|
||||
, mApplyConversion(PR_TRUE)
|
||||
, mCanceled(PR_FALSE)
|
||||
, mIsPending(PR_FALSE)
|
||||
|
@ -1405,6 +1406,19 @@ HttpBaseChannel::SetupReplacementChannel(nsIURI *newURI,
|
|||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
NS_IMPL_ISUPPORTS0(HttpBaseChannel::CacheEntryClosePreventer)
|
||||
|
||||
HttpBaseChannel::CacheEntryClosePreventer::CacheEntryClosePreventer(
|
||||
HttpBaseChannel* channel)
|
||||
: mChannel(channel)
|
||||
{
|
||||
mChannel->OnIncreaseCacheEntryClosePreventCount();
|
||||
}
|
||||
|
||||
HttpBaseChannel::CacheEntryClosePreventer::~CacheEntryClosePreventer()
|
||||
{
|
||||
mChannel->OnDecreaseCacheEntryClosePreventCount();
|
||||
}
|
||||
|
||||
} // namespace net
|
||||
} // namespace mozilla
|
||||
|
||||
|
|
|
@ -186,10 +186,30 @@ public:
|
|||
PRPackedBool mReady;
|
||||
};
|
||||
|
||||
nsHttpResponseHead * GetResponseHead() const { return mResponseHead; }
|
||||
nsHttpRequestHead * GetRequestHead() { return &mRequestHead; }
|
||||
nsHttpResponseHead * GetResponseHead() const { return mResponseHead; }
|
||||
nsHttpRequestHead * GetRequestHead() { return &mRequestHead; }
|
||||
|
||||
protected:
|
||||
// Increment/decrement counter that, when positive, keeps channel's cache
|
||||
// entry open after OnStopRequest if needed.
|
||||
|
||||
virtual void OnIncreaseCacheEntryClosePreventCount() = 0;
|
||||
virtual void OnDecreaseCacheEntryClosePreventCount() = 0;
|
||||
|
||||
// Object created and returned on every call to cacheEntryClosePreventer
|
||||
// attribute. Calls the two methods right above in its constructor and
|
||||
// destructor respectively.
|
||||
|
||||
class CacheEntryClosePreventer : public nsISupports
|
||||
{
|
||||
public:
|
||||
NS_DECL_ISUPPORTS
|
||||
CacheEntryClosePreventer(HttpBaseChannel* channel);
|
||||
private:
|
||||
~CacheEntryClosePreventer();
|
||||
nsRefPtr<HttpBaseChannel> mChannel;
|
||||
};
|
||||
|
||||
nsresult ApplyContentConversions();
|
||||
|
||||
void AddCookiesToRequest();
|
||||
|
@ -238,6 +258,10 @@ protected:
|
|||
PRUint8 mCaps;
|
||||
PRUint8 mRedirectionLimit;
|
||||
|
||||
// Keeps the number of CacheEntryClosePreventer objects being held,
|
||||
// positive value prevents the cache entry from release in OnStopRequest.
|
||||
PRUint32 mCacheEntryClosePreventionCount;
|
||||
|
||||
PRUint32 mApplyConversion : 1;
|
||||
PRUint32 mCanceled : 1;
|
||||
PRUint32 mIsPending : 1;
|
||||
|
|
|
@ -66,7 +66,7 @@ HttpChannelChild::HttpChannelChild()
|
|||
, mSendResumeAt(false)
|
||||
, mSuspendCount(0)
|
||||
, mIPCOpen(false)
|
||||
, mKeptAlive(false)
|
||||
, mDeferredIPDLClose(false)
|
||||
{
|
||||
LOG(("Creating HttpChannelChild @%x\n", this));
|
||||
}
|
||||
|
@ -90,8 +90,8 @@ NS_IMETHODIMP_(nsrefcnt) HttpChannelChild::Release()
|
|||
--mRefCnt;
|
||||
NS_LOG_RELEASE(this, mRefCnt, "HttpChannelChild");
|
||||
|
||||
if (mRefCnt == 1 && mKeptAlive && mIPCOpen) {
|
||||
mKeptAlive = false;
|
||||
if (mRefCnt == 1 && mDeferredIPDLClose && mIPCOpen) {
|
||||
mDeferredIPDLClose = false;
|
||||
// Send_delete calls NeckoChild::DeallocPHttpChannel, which will release
|
||||
// again to refcount==0
|
||||
PHttpChannelChild::Send__delete__(this);
|
||||
|
@ -112,6 +112,7 @@ NS_INTERFACE_MAP_BEGIN(HttpChannelChild)
|
|||
NS_INTERFACE_MAP_ENTRY(nsIHttpChannel)
|
||||
NS_INTERFACE_MAP_ENTRY(nsIHttpChannelInternal)
|
||||
NS_INTERFACE_MAP_ENTRY(nsICacheInfoChannel)
|
||||
NS_INTERFACE_MAP_ENTRY(nsICacheInfoChannel_GECKO_2_0)
|
||||
NS_INTERFACE_MAP_ENTRY(nsIResumableChannel)
|
||||
NS_INTERFACE_MAP_ENTRY(nsISupportsPriority)
|
||||
NS_INTERFACE_MAP_ENTRY(nsIProxiedChannel)
|
||||
|
@ -391,16 +392,7 @@ HttpChannelChild::OnStopRequest(const nsresult& statusCode)
|
|||
mLoadGroup->RemoveRequest(this, nsnull, mStatus);
|
||||
}
|
||||
|
||||
if (!(mLoadFlags & LOAD_DOCUMENT_URI)) {
|
||||
// This calls NeckoChild::DeallocPHttpChannel(), which deletes |this| if IPDL
|
||||
// holds the last reference. Don't rely on |this| existing after here.
|
||||
PHttpChannelChild::Send__delete__(this);
|
||||
} else {
|
||||
// We need to keep the document loading channel alive for further
|
||||
// communication, mainly for collecting a security state values.
|
||||
mKeptAlive = true;
|
||||
SendDocumentChannelCleanup();
|
||||
}
|
||||
MaybeCloseIPDL();
|
||||
}
|
||||
|
||||
class ProgressEvent : public ChannelEvent
|
||||
|
@ -557,8 +549,7 @@ HttpChannelChild::OnCancel(const nsresult& status)
|
|||
mListener = NULL;
|
||||
mListenerContext = NULL;
|
||||
|
||||
if (mIPCOpen)
|
||||
PHttpChannelChild::Send__delete__(this);
|
||||
MaybeCloseIPDL(true /* Force document channel deletion */);
|
||||
}
|
||||
|
||||
class DeleteSelfEvent : public ChannelEvent
|
||||
|
@ -584,7 +575,34 @@ HttpChannelChild::RecvDeleteSelf()
|
|||
void
|
||||
HttpChannelChild::DeleteSelf()
|
||||
{
|
||||
Send__delete__(this);
|
||||
MaybeCloseIPDL(true /* Force document channel deletion */);
|
||||
}
|
||||
|
||||
void
|
||||
HttpChannelChild::MaybeCloseIPDL(bool forceDocumentLoadDeletion)
|
||||
{
|
||||
if (mCacheEntryClosePreventionCount) {
|
||||
// Someone is still holding the cache close prevention lock, keep this
|
||||
// channel alive to be able to communicate lock release to the parent.
|
||||
mDeferredIPDLClose = true;
|
||||
return;
|
||||
}
|
||||
|
||||
if ((mLoadFlags & LOAD_DOCUMENT_URI) && !forceDocumentLoadDeletion) {
|
||||
// We need to keep the document loading channel alive for further
|
||||
// communication, mainly for collecting a security state values.
|
||||
mDeferredIPDLClose = true;
|
||||
if (mIPCOpen)
|
||||
SendDocumentChannelCleanup();
|
||||
return;
|
||||
}
|
||||
|
||||
// No need to keep the child channel, delete it.
|
||||
// This calls NeckoChild::DeallocPHttpChannel(), which deletes |this| if IPDL
|
||||
// holds the last reference. Don't rely on |this| existing after here.
|
||||
mDeferredIPDLClose = false;
|
||||
if (mIPCOpen)
|
||||
PHttpChannelChild::Send__delete__(this);
|
||||
}
|
||||
|
||||
class Redirect1Event : public ChannelEvent
|
||||
|
@ -996,6 +1014,21 @@ HttpChannelChild::SetRequestHeader(const nsACString& aHeader,
|
|||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
HttpChannelChild::SetResponseHeader(const nsACString& aHeader,
|
||||
const nsACString& aValue,
|
||||
PRBool aMerge)
|
||||
{
|
||||
nsresult rv = HttpBaseChannel::SetResponseHeader(aHeader, aValue, aMerge);
|
||||
if (NS_FAILED(rv))
|
||||
return rv;
|
||||
|
||||
nsCString header(aHeader);
|
||||
nsCString value(aValue);
|
||||
SendSetResponseHeader(header, value, aMerge);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// HttpChannelChild::nsIHttpChannelInternal
|
||||
//-----------------------------------------------------------------------------
|
||||
|
@ -1053,6 +1086,36 @@ HttpChannelChild::IsFromCache(PRBool *value)
|
|||
return NS_OK;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// nsHttpChannel::nsICacheInfoChannel_GECKO_2_0
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
NS_IMETHODIMP
|
||||
HttpChannelChild::GetCacheEntryClosePreventer(nsISupports** _retval)
|
||||
{
|
||||
NS_ADDREF(*_retval = new CacheEntryClosePreventer(this));
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
void
|
||||
HttpChannelChild::OnIncreaseCacheEntryClosePreventCount()
|
||||
{
|
||||
LOG(("HttpChannelChild::mCacheEntryClosePreventionCount increased to %d, [this=%x]",
|
||||
mCacheEntryClosePreventionCount, this));
|
||||
++mCacheEntryClosePreventionCount;
|
||||
}
|
||||
|
||||
void
|
||||
HttpChannelChild::OnDecreaseCacheEntryClosePreventCount()
|
||||
{
|
||||
LOG(("HttpChannelChild::mCacheEntryClosePreventionCount decreased to %d, [this=%x]",
|
||||
mCacheEntryClosePreventionCount, this));
|
||||
--mCacheEntryClosePreventionCount;
|
||||
|
||||
if (!mCacheEntryClosePreventionCount && mDeferredIPDLClose)
|
||||
MaybeCloseIPDL();
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// HttpChannelChild::nsIResumableChannel
|
||||
//-----------------------------------------------------------------------------
|
||||
|
|
|
@ -70,6 +70,7 @@ namespace net {
|
|||
class HttpChannelChild : public PHttpChannelChild
|
||||
, public HttpBaseChannel
|
||||
, public nsICacheInfoChannel
|
||||
, public nsICacheInfoChannel_GECKO_2_0
|
||||
, public nsIProxiedChannel
|
||||
, public nsITraceableChannel
|
||||
, public nsIApplicationCacheChannel
|
||||
|
@ -82,6 +83,7 @@ class HttpChannelChild : public PHttpChannelChild
|
|||
public:
|
||||
NS_DECL_ISUPPORTS_INHERITED
|
||||
NS_DECL_NSICACHEINFOCHANNEL
|
||||
NS_DECL_NSICACHEINFOCHANNEL_GECKO_2_0
|
||||
NS_DECL_NSIPROXIEDCHANNEL
|
||||
NS_DECL_NSITRACEABLECHANNEL
|
||||
NS_DECL_NSIAPPLICATIONCACHECONTAINER
|
||||
|
@ -107,6 +109,9 @@ public:
|
|||
NS_IMETHOD SetRequestHeader(const nsACString& aHeader,
|
||||
const nsACString& aValue,
|
||||
PRBool aMerge);
|
||||
NS_IMETHOD SetResponseHeader(const nsACString& aHeader,
|
||||
const nsACString& aValue,
|
||||
PRBool aMerge);
|
||||
// nsIHttpChannelInternal
|
||||
NS_IMETHOD SetupFallbackChannel(const char *aFallbackKey);
|
||||
// nsISupportsPriority
|
||||
|
@ -166,7 +171,11 @@ private:
|
|||
PRUint32 mSuspendCount;
|
||||
|
||||
bool mIPCOpen;
|
||||
bool mKeptAlive;
|
||||
// Indicates IPDL channel was not deleted during OnStopRequest, because
|
||||
// 1) this a document-level channel (IPDL channel will be deleted during
|
||||
// destructor); or 2) mCacheEntryClosePreventionCount is non-zero (IPDL
|
||||
// channel will be deleted when count hits 0).
|
||||
bool mDeferredIPDLClose;
|
||||
|
||||
void OnStartRequest(const nsHttpResponseHead& responseHead,
|
||||
const PRBool& useResponseHead,
|
||||
|
@ -190,6 +199,11 @@ private:
|
|||
void Redirect3Complete();
|
||||
void DeleteSelf();
|
||||
|
||||
void MaybeCloseIPDL(bool forceDocumentLoadDeletion = false);
|
||||
|
||||
virtual void OnIncreaseCacheEntryClosePreventCount();
|
||||
virtual void OnDecreaseCacheEntryClosePreventCount();
|
||||
|
||||
friend class StartRequestEvent;
|
||||
friend class StopRequestEvent;
|
||||
friend class DataAvailableEvent;
|
||||
|
|
|
@ -85,6 +85,9 @@ HttpChannelParent::ActorDestroy(ActorDestroyReason why)
|
|||
// We may still have refcount>0 if nsHttpChannel hasn't called OnStopRequest
|
||||
// yet, but we must not send any more msgs to child.
|
||||
mIPCClosed = true;
|
||||
|
||||
// As we know the child channel has finished, let the cache entry close.
|
||||
mCacheClosePreventer = 0;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
@ -297,9 +300,25 @@ HttpChannelParent::RecvCancel(const nsresult& status)
|
|||
bool
|
||||
HttpChannelParent::RecvSetCacheTokenCachedCharset(const nsCString& charset)
|
||||
{
|
||||
if (mCacheDescriptor)
|
||||
mCacheDescriptor->SetMetaDataElement("charset",
|
||||
PromiseFlatCString(charset).get());
|
||||
nsHttpChannel *chan = static_cast<nsHttpChannel *>(mChannel.get());
|
||||
|
||||
nsresult rv;
|
||||
|
||||
nsCOMPtr<nsICacheEntryDescriptor> cacheDescriptor;
|
||||
rv = chan->GetCacheToken(getter_AddRefs(cacheDescriptor));
|
||||
if (NS_SUCCEEDED(rv))
|
||||
cacheDescriptor->SetMetaDataElement("charset",
|
||||
PromiseFlatCString(charset).get());
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
HttpChannelParent::RecvSetResponseHeader(const nsCString& header,
|
||||
const nsCString& value,
|
||||
const bool& merge)
|
||||
{
|
||||
nsHttpChannel *chan = static_cast<nsHttpChannel *>(mChannel.get());
|
||||
chan->SetResponseHeader(header, value, merge);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -353,7 +372,7 @@ HttpChannelParent::RecvDocumentChannelCleanup()
|
|||
{
|
||||
// We must clear the cache entry here, else we'll block other channels from
|
||||
// reading it if we've got it open for writing.
|
||||
mCacheDescriptor = 0;
|
||||
mCacheClosePreventer = 0;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
@ -405,9 +424,14 @@ HttpChannelParent::OnStartRequest(nsIRequest *aRequest, nsISupports *aContext)
|
|||
if (encodedChannel)
|
||||
encodedChannel->SetApplyConversion(PR_FALSE);
|
||||
|
||||
// Keep the cache entry for future use in RecvSetCacheTokenCachedCharset().
|
||||
// It could be already released by nsHttpChannel at that time.
|
||||
chan->GetCacheToken(getter_AddRefs(mCacheDescriptor));
|
||||
// Prevent cache entry from being closed during HttpChannel::OnStopRequest:
|
||||
// - We need the cache entry for RecvSetCacheTokenCachedCharset()
|
||||
// - The child channel may call GetCacheEntryClosePreventer, so we have to
|
||||
// call it now (otherwise we could hit OnStopRequest and close entry
|
||||
// before child gets a chance to keep it open).
|
||||
// We close entry either when RecvDocumentChannelCleanup is called, or the
|
||||
// IPDL channel is deleted.
|
||||
chan->GetCacheEntryClosePreventer(getter_AddRefs(mCacheClosePreventer));
|
||||
|
||||
nsCString secInfoSerialization;
|
||||
nsCOMPtr<nsISupports> secInfoSupp;
|
||||
|
@ -428,12 +452,15 @@ HttpChannelParent::OnStartRequest(nsIRequest *aRequest, nsISupports *aContext)
|
|||
tuple->mMerge = false;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsICacheEntryDescriptor> cacheDescriptor;
|
||||
chan->GetCacheToken(getter_AddRefs(cacheDescriptor));
|
||||
|
||||
if (mIPCClosed ||
|
||||
!SendOnStartRequest(responseHead ? *responseHead : nsHttpResponseHead(),
|
||||
!!responseHead,
|
||||
headers,
|
||||
isFromCache,
|
||||
mCacheDescriptor ? PR_TRUE : PR_FALSE,
|
||||
cacheDescriptor ? PR_TRUE : PR_FALSE,
|
||||
expirationTime, cachedCharset, secInfoSerialization))
|
||||
{
|
||||
return NS_ERROR_UNEXPECTED;
|
||||
|
|
|
@ -99,6 +99,9 @@ protected:
|
|||
virtual bool RecvConnectChannel(const PRUint32& channelId);
|
||||
virtual bool RecvSetPriority(const PRUint16& priority);
|
||||
virtual bool RecvSetCacheTokenCachedCharset(const nsCString& charset);
|
||||
virtual bool RecvSetResponseHeader(const nsCString& header,
|
||||
const nsCString& value,
|
||||
const bool& merge);
|
||||
virtual bool RecvSuspend();
|
||||
virtual bool RecvResume();
|
||||
virtual bool RecvCancel(const nsresult& status);
|
||||
|
@ -119,7 +122,7 @@ protected:
|
|||
|
||||
private:
|
||||
nsCOMPtr<nsIChannel> mChannel;
|
||||
nsCOMPtr<nsICacheEntryDescriptor> mCacheDescriptor;
|
||||
nsCOMPtr<nsISupports> mCacheClosePreventer;
|
||||
bool mIPCClosed; // PHttpChannel actor has been Closed()
|
||||
|
||||
nsCOMPtr<nsIChannel> mRedirectChannel;
|
||||
|
|
|
@ -86,6 +86,7 @@ parent:
|
|||
SetPriority(PRUint16 priority);
|
||||
|
||||
SetCacheTokenCachedCharset(nsCString charset);
|
||||
SetResponseHeader(nsCString header, nsCString value, bool merge);
|
||||
|
||||
UpdateAssociatedContentSecurity(PRInt32 high,
|
||||
PRInt32 low,
|
||||
|
|
|
@ -135,6 +135,8 @@ nsHttpChannel::nsHttpChannel()
|
|||
, mFallingBack(PR_FALSE)
|
||||
, mWaitingForRedirectCallback(PR_FALSE)
|
||||
, mRequestTimeInitialized(PR_FALSE)
|
||||
, mDeferredCacheEntryClose(PR_FALSE)
|
||||
, mDoomCacheEntryOnClose(PR_FALSE)
|
||||
{
|
||||
LOG(("Creating nsHttpChannel [this=%p]\n", this));
|
||||
}
|
||||
|
@ -2828,12 +2830,34 @@ nsHttpChannel::ReadFromCache()
|
|||
void
|
||||
nsHttpChannel::CloseCacheEntry(PRBool doomOnFailure)
|
||||
{
|
||||
if (!mCacheEntry)
|
||||
if (!mCacheEntry || mDeferredCacheEntryClose)
|
||||
return;
|
||||
|
||||
LOG(("nsHttpChannel::CloseCacheEntry [this=%p] mStatus=%x mCacheAccess=%x",
|
||||
this, mStatus, mCacheAccess));
|
||||
|
||||
mDoomCacheEntryOnClose = doomOnFailure;
|
||||
|
||||
if (mCacheEntryClosePreventionCount) {
|
||||
LOG((" close is on hold"));
|
||||
mDeferredCacheEntryClose = PR_TRUE;
|
||||
return;
|
||||
}
|
||||
|
||||
CloseCacheEntryInternal();
|
||||
}
|
||||
|
||||
void
|
||||
nsHttpChannel::CloseCacheEntryInternal()
|
||||
{
|
||||
LOG(("nsHttpChannel::CloseCacheEntryInternal [this=%p] mStatus=%x mCacheAccess=%x",
|
||||
this, mStatus, mCacheAccess));
|
||||
|
||||
// perform any final cache operations before we close the cache entry.
|
||||
if ((mCacheAccess & nsICache::ACCESS_WRITE) && mRequestTimeInitialized) {
|
||||
FinalizeCacheEntry();
|
||||
}
|
||||
|
||||
// If we have begun to create or replace a cache entry, and that cache
|
||||
// entry is not complete and not resumable, then it needs to be doomed.
|
||||
// Otherwise, CheckCache will make the mistake of thinking that the
|
||||
|
@ -2842,7 +2866,7 @@ nsHttpChannel::CloseCacheEntry(PRBool doomOnFailure)
|
|||
PRBool doom = PR_FALSE;
|
||||
if (mInitedCacheEntry) {
|
||||
NS_ASSERTION(mResponseHead, "oops");
|
||||
if (NS_FAILED(mStatus) && doomOnFailure &&
|
||||
if (NS_FAILED(mStatus) && mDoomCacheEntryOnClose &&
|
||||
(mCacheAccess & nsICache::ACCESS_WRITE) &&
|
||||
!mResponseHead->IsResumable())
|
||||
doom = PR_TRUE;
|
||||
|
@ -3490,6 +3514,7 @@ NS_INTERFACE_MAP_BEGIN(nsHttpChannel)
|
|||
NS_INTERFACE_MAP_ENTRY(nsIRequestObserver)
|
||||
NS_INTERFACE_MAP_ENTRY(nsIStreamListener)
|
||||
NS_INTERFACE_MAP_ENTRY(nsIHttpChannel)
|
||||
NS_INTERFACE_MAP_ENTRY(nsICacheInfoChannel_GECKO_2_0)
|
||||
NS_INTERFACE_MAP_ENTRY(nsICacheInfoChannel)
|
||||
NS_INTERFACE_MAP_ENTRY(nsICachingChannel)
|
||||
NS_INTERFACE_MAP_ENTRY(nsIUploadChannel)
|
||||
|
@ -4019,12 +4044,6 @@ nsHttpChannel::OnStopRequest(nsIRequest *request, nsISupports *ctxt, nsresult st
|
|||
mIsPending = PR_FALSE;
|
||||
mStatus = status;
|
||||
|
||||
// perform any final cache operations before we close the cache entry.
|
||||
if (mCacheEntry && (mCacheAccess & nsICache::ACCESS_WRITE) &&
|
||||
mRequestTimeInitialized){
|
||||
FinalizeCacheEntry();
|
||||
}
|
||||
|
||||
if (mListener) {
|
||||
LOG((" calling OnStopRequest\n"));
|
||||
mListener->OnStopRequest(this, mListenerContext, status);
|
||||
|
@ -4207,6 +4226,37 @@ nsHttpChannel::SetCacheTokenCachedCharset(const nsACString &aCharset)
|
|||
PromiseFlatCString(aCharset).get());
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// nsHttpChannel::nsIHttpChannelInternal_GECKO_2_0
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsHttpChannel::GetCacheEntryClosePreventer(nsISupports** _retval)
|
||||
{
|
||||
NS_ADDREF(*_retval = new CacheEntryClosePreventer(this));
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
void
|
||||
nsHttpChannel::OnIncreaseCacheEntryClosePreventCount()
|
||||
{
|
||||
++mCacheEntryClosePreventionCount;
|
||||
LOG(("nsHttpChannel::mCacheEntryClosePreventionCount increased to %d, [this=%x]",
|
||||
mCacheEntryClosePreventionCount, this));
|
||||
}
|
||||
|
||||
void
|
||||
nsHttpChannel::OnDecreaseCacheEntryClosePreventCount()
|
||||
{
|
||||
--mCacheEntryClosePreventionCount;
|
||||
LOG(("nsHttpChannel::mCacheEntryClosePreventionCount decreased to %d, [this=%x]",
|
||||
mCacheEntryClosePreventionCount, this));
|
||||
|
||||
if (!mCacheEntryClosePreventionCount && mDeferredCacheEntryClose) {
|
||||
CloseCacheEntryInternal();
|
||||
}
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// nsHttpChannel::nsICachingChannel
|
||||
//-----------------------------------------------------------------------------
|
||||
|
|
|
@ -51,6 +51,7 @@
|
|||
#include "nsTArray.h"
|
||||
|
||||
#include "nsIHttpEventSink.h"
|
||||
#include "nsICacheInfoChannel.h"
|
||||
#include "nsICachingChannel.h"
|
||||
#include "nsICacheEntryDescriptor.h"
|
||||
#include "nsICacheListener.h"
|
||||
|
@ -67,6 +68,7 @@
|
|||
|
||||
class nsAHttpConnection;
|
||||
class AutoRedirectVetoNotifier;
|
||||
class HttpChannelCacheEntryClosePreventer;
|
||||
|
||||
using namespace mozilla::net;
|
||||
|
||||
|
@ -76,6 +78,7 @@ using namespace mozilla::net;
|
|||
|
||||
class nsHttpChannel : public HttpBaseChannel
|
||||
, public nsIStreamListener
|
||||
, public nsICacheInfoChannel_GECKO_2_0
|
||||
, public nsICachingChannel
|
||||
, public nsICacheListener
|
||||
, public nsITransportEventSink
|
||||
|
@ -89,6 +92,7 @@ public:
|
|||
NS_DECL_ISUPPORTS_INHERITED
|
||||
NS_DECL_NSIREQUESTOBSERVER
|
||||
NS_DECL_NSISTREAMLISTENER
|
||||
NS_DECL_NSICACHEINFOCHANNEL_GECKO_2_0
|
||||
NS_DECL_NSICACHEINFOCHANNEL
|
||||
NS_DECL_NSICACHINGCHANNEL
|
||||
NS_DECL_NSICACHELISTENER
|
||||
|
@ -229,6 +233,7 @@ private:
|
|||
nsresult ShouldUpdateOfflineCacheEntry(PRBool *shouldCacheForOfflineUse);
|
||||
nsresult ReadFromCache();
|
||||
void CloseCacheEntry(PRBool doomOnFailure);
|
||||
void CloseCacheEntryInternal();
|
||||
void CloseOfflineCacheEntry();
|
||||
nsresult InitCacheEntry();
|
||||
nsresult InitOfflineCacheEntry();
|
||||
|
@ -271,6 +276,9 @@ private:
|
|||
*/
|
||||
nsresult Hash(const char *buf, nsACString &hash);
|
||||
|
||||
virtual void OnIncreaseCacheEntryClosePreventCount();
|
||||
virtual void OnDecreaseCacheEntryClosePreventCount();
|
||||
|
||||
private:
|
||||
nsCOMPtr<nsISupports> mSecurityInfo;
|
||||
nsCOMPtr<nsICancelable> mProxyRequest;
|
||||
|
@ -345,7 +353,12 @@ private:
|
|||
PRUint32 mWaitingForRedirectCallback : 1;
|
||||
// True if mRequestTime has been set. In such a case it is safe to update
|
||||
// the cache entry's expiration time. Otherwise, it is not(see bug 567360).
|
||||
PRUint32 mRequestTimeInitialized : 1;
|
||||
PRUint32 mRequestTimeInitialized : 1;
|
||||
// True if CloseCacheEntry was called while cache entry hold counter was
|
||||
// positive.
|
||||
PRUint32 mDeferredCacheEntryClose : 1;
|
||||
// True if CloseCacheEntry was called with doomOnFailure set to TRUE.
|
||||
PRUint32 mDoomCacheEntryOnClose : 1;
|
||||
|
||||
nsTArray<nsContinueRedirectionFunc> mRedirectFuncStack;
|
||||
|
||||
|
|
|
@ -41,6 +41,7 @@
|
|||
#include "nsHtml5StreamParser.h"
|
||||
#include "nsICharsetConverterManager.h"
|
||||
#include "nsICharsetAlias.h"
|
||||
#include "nsICacheInfoChannel.h"
|
||||
#include "nsServiceManagerUtils.h"
|
||||
#include "nsEncoderDecoderUtils.h"
|
||||
#include "nsContentUtils.h"
|
||||
|
@ -220,6 +221,7 @@ nsHtml5StreamParser::~nsHtml5StreamParser()
|
|||
mTokenizer->end();
|
||||
NS_ASSERTION(!mFlushTimer, "Flush timer was not dropped before dtor!");
|
||||
#ifdef DEBUG
|
||||
mCacheEntryClosePreventer = nsnull;
|
||||
mRequest = nsnull;
|
||||
mObserver = nsnull;
|
||||
mUnicodeDecoder = nsnull;
|
||||
|
@ -558,6 +560,17 @@ nsHtml5StreamParser::OnStartRequest(nsIRequest* aRequest, nsISupports* aContext)
|
|||
}
|
||||
mRequest = aRequest;
|
||||
|
||||
// We must keep the cache entry hold lock to prevent the channel from
|
||||
// dropping the cache entry after OnStopRequest. We may need to modify
|
||||
// the cache entry asynchronously, after OnStopRequest.
|
||||
// See bug 579846.
|
||||
nsCOMPtr<nsICacheInfoChannel_GECKO_2_0> cacheInfoChannel =
|
||||
do_QueryInterface(aRequest);
|
||||
if (cacheInfoChannel) {
|
||||
cacheInfoChannel->
|
||||
GetCacheEntryClosePreventer(getter_AddRefs(mCacheEntryClosePreventer));
|
||||
}
|
||||
|
||||
mStreamState = STREAM_BEING_READ;
|
||||
|
||||
PRBool scriptingEnabled = mExecutor->IsScriptEnabled();
|
||||
|
|
|
@ -331,6 +331,7 @@ class nsHtml5StreamParser : public nsIStreamListener,
|
|||
|
||||
nsCOMPtr<nsIRequest> mRequest;
|
||||
nsCOMPtr<nsIRequestObserver> mObserver;
|
||||
nsCOMPtr<nsISupports> mCacheEntryClosePreventer;
|
||||
|
||||
/**
|
||||
* The Unicode decoder
|
||||
|
|
|
@ -84,6 +84,8 @@ _TEST_FILES = parser_datreader.js \
|
|||
file_bug594730-9.html \
|
||||
test_bug599584.html \
|
||||
iframe_bug599584.html \
|
||||
test_bug579846.html \
|
||||
sub_bug579846.sjs \
|
||||
$(NULL)
|
||||
|
||||
# Disabled test due to orange on Linux
|
||||
|
|
|
@ -0,0 +1,17 @@
|
|||
function handleRequest(request, response)
|
||||
{
|
||||
var date = new Date();
|
||||
var now = date.getTime();
|
||||
date.setTime(date.getTime() + 5 * 60 * 1000);
|
||||
var exp = (date).toGMTString();
|
||||
|
||||
response.setHeader("Content-Type", "text/html", false);
|
||||
response.setHeader("Expires", exp, false);
|
||||
|
||||
response.write(
|
||||
"<html>\n" +
|
||||
"<head><meta http-equiv='Cache-control' content='no-cache'></head>\n" +
|
||||
"<body onload='parent.testFrameOnLoad(document.body.innerHTML)'>" + now + "</body>" +
|
||||
"</html>"
|
||||
);
|
||||
}
|
|
@ -0,0 +1,47 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<!--
|
||||
https://bugzilla.mozilla.org/show_bug.cgi?id=579846
|
||||
-->
|
||||
<head>
|
||||
<title>Test for Bug 579846</title>
|
||||
<script type="text/javascript" src="/MochiKit/packed.js"></script>
|
||||
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
|
||||
<script type="text/javascript">
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
|
||||
var nextAction;
|
||||
function testFrameOnLoad(content)
|
||||
{
|
||||
nextAction(content);
|
||||
}
|
||||
|
||||
nextAction = function(acontent)
|
||||
{
|
||||
nextAction = function(bcontent)
|
||||
{
|
||||
ok(parseInt(acontent) < parseInt(bcontent), "Content has changed");
|
||||
SimpleTest.finish();
|
||||
}
|
||||
|
||||
// Give a chance to update
|
||||
window.setTimeout(function() {
|
||||
var testFrame = document.getElementById('_test_iframe2');
|
||||
testFrame.contentWindow.location.href = 'sub_bug579846.sjs';
|
||||
}, 100);
|
||||
}
|
||||
|
||||
window.onload = function()
|
||||
{
|
||||
var testFrame = document.getElementById('_test_iframe1');
|
||||
testFrame.contentWindow.location.href = 'sub_bug579846.sjs';
|
||||
}
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
<iframe src="" id="_test_iframe1"></iframe>
|
||||
<iframe src="" id="_test_iframe2"></iframe>
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -2188,7 +2188,7 @@ HUD_SERVICE.prototype =
|
|||
});
|
||||
|
||||
// Store the loggedNode and the httpActivity object for later reuse.
|
||||
let linkNode = loggedNode.querySelector(".webconsole-msg-url");
|
||||
let linkNode = loggedNode.querySelector(".webconsole-msg-link");
|
||||
|
||||
httpActivity.messageObject = {
|
||||
messageNode: loggedNode,
|
||||
|
@ -2236,7 +2236,7 @@ HUD_SERVICE.prototype =
|
|||
let msgObject = httpActivity.messageObject;
|
||||
|
||||
let updatePanel = false;
|
||||
let data, textNode;
|
||||
let data;
|
||||
// Store the time information for this activity subtype.
|
||||
httpActivity.timing[transCodes[aActivitySubtype]] = aTimestamp;
|
||||
|
||||
|
@ -2286,25 +2286,17 @@ HUD_SERVICE.prototype =
|
|||
httpActivity.response.status =
|
||||
aExtraStringData.split(/\r\n|\n|\r/)[0];
|
||||
|
||||
// Remove the text node from the URL node and add a new one that
|
||||
// contains the response status.
|
||||
textNode = msgObject.linkNode.firstChild;
|
||||
textNode.parentNode.removeChild(textNode);
|
||||
// Add the response status.
|
||||
let linkNode = msgObject.linkNode;
|
||||
let statusNode = linkNode.
|
||||
querySelector(".webconsole-msg-status");
|
||||
let statusText = "[" + httpActivity.response.status + "]";
|
||||
statusNode.setAttribute("value", statusText);
|
||||
|
||||
data = [ httpActivity.url,
|
||||
httpActivity.response.status ];
|
||||
|
||||
// Format the pieces of data. The result will be something like
|
||||
// "http://example.com/ [HTTP/1.0 200 OK]".
|
||||
let text = self.getFormatStr("networkUrlWithStatus", data);
|
||||
|
||||
// Replace the displayed text and the clipboard text with the
|
||||
// new data.
|
||||
let chromeDocument = msgObject.messageNode.ownerDocument;
|
||||
msgObject.linkNode.appendChild(
|
||||
chromeDocument.createTextNode(text));
|
||||
let clipboardTextPieces =
|
||||
[ httpActivity.method, httpActivity.url, statusText ];
|
||||
msgObject.messageNode.clipboardText =
|
||||
msgObject.messageNode.textContent;
|
||||
clipboardTextPieces.join(" ");
|
||||
|
||||
let status = parseInt(httpActivity.response.status.
|
||||
replace(/^HTTP\/\d\.\d (\d+).+$/, "$1"));
|
||||
|
@ -2325,27 +2317,21 @@ HUD_SERVICE.prototype =
|
|||
Math.round((timing.RESPONSE_COMPLETE -
|
||||
timing.REQUEST_HEADER) / 1000);
|
||||
|
||||
// Remove the text node from the link node and add a new one
|
||||
// that contains the request duration.
|
||||
textNode = msgObject.linkNode.firstChild;
|
||||
textNode.parentNode.removeChild(textNode);
|
||||
// Add the request duration.
|
||||
let linkNode = msgObject.linkNode;
|
||||
let statusNode = linkNode.
|
||||
querySelector(".webconsole-msg-status");
|
||||
|
||||
data = [ httpActivity.url,
|
||||
httpActivity.response.status,
|
||||
requestDuration ];
|
||||
let statusText = httpActivity.response.status;
|
||||
let timeText = self.getFormatStr("NetworkPanel.durationMS",
|
||||
[ requestDuration ]);
|
||||
let fullStatusText = "[" + statusText + " " + timeText + "]";
|
||||
statusNode.setAttribute("value", fullStatusText);
|
||||
|
||||
// Format the pieces of data. The result will be something like
|
||||
// "http://example.com/ [HTTP/1.0 200 OK 200 ms]".
|
||||
let text = self.getFormatStr("networkUrlWithStatusAndDuration",
|
||||
data);
|
||||
|
||||
// Replace the displayed text and the clipboard text with the
|
||||
// new data.
|
||||
let chromeDocument = msgObject.messageNode.ownerDocument;
|
||||
msgObject.linkNode.appendChild(
|
||||
chromeDocument.createTextNode(text));
|
||||
let clipboardTextPieces =
|
||||
[ httpActivity.method, httpActivity.url, fullStatusText ];
|
||||
msgObject.messageNode.clipboardText =
|
||||
msgObject.messageNode.textContent;
|
||||
clipboardTextPieces.join(" ");
|
||||
|
||||
delete self.openRequests[item.id];
|
||||
updatePanel = true;
|
||||
|
@ -2452,19 +2438,36 @@ HUD_SERVICE.prototype =
|
|||
let outputNode = this.hudReferences[hudId].outputNode;
|
||||
|
||||
let chromeDocument = outputNode.ownerDocument;
|
||||
let msgNode = chromeDocument.createElementNS(HTML_NS, "html:span");
|
||||
let msgNode = chromeDocument.createElementNS(XUL_NS, "xul:hbox");
|
||||
|
||||
// Create the method part of the message (e.g. "GET").
|
||||
let method = chromeDocument.createTextNode(aActivityObject.method + " ");
|
||||
msgNode.appendChild(method);
|
||||
let methodNode = chromeDocument.createElementNS(XUL_NS, "xul:label");
|
||||
methodNode.setAttribute("value", aActivityObject.method);
|
||||
methodNode.classList.add("webconsole-msg-body-piece");
|
||||
msgNode.appendChild(methodNode);
|
||||
|
||||
// Create the clickable URL part of the message.
|
||||
let linkNode = chromeDocument.createElementNS(HTML_NS, "html:span");
|
||||
linkNode.appendChild(chromeDocument.createTextNode(aActivityObject.url));
|
||||
linkNode.classList.add("hud-clickable");
|
||||
linkNode.classList.add("webconsole-msg-url");
|
||||
let linkNode = chromeDocument.createElementNS(XUL_NS, "xul:hbox");
|
||||
linkNode.setAttribute("flex", "1");
|
||||
linkNode.classList.add("webconsole-msg-body-piece");
|
||||
linkNode.classList.add("webconsole-msg-link");
|
||||
msgNode.appendChild(linkNode);
|
||||
|
||||
let urlNode = chromeDocument.createElementNS(XUL_NS, "xul:label");
|
||||
urlNode.setAttribute("crop", "center");
|
||||
urlNode.setAttribute("flex", "1");
|
||||
urlNode.setAttribute("title", aActivityObject.url);
|
||||
urlNode.setAttribute("value", aActivityObject.url);
|
||||
urlNode.classList.add("hud-clickable");
|
||||
urlNode.classList.add("webconsole-msg-body-piece");
|
||||
urlNode.classList.add("webconsole-msg-url");
|
||||
linkNode.appendChild(urlNode);
|
||||
|
||||
let statusNode = chromeDocument.createElementNS(XUL_NS, "xul:label");
|
||||
statusNode.setAttribute("value", "");
|
||||
statusNode.classList.add("hud-clickable");
|
||||
statusNode.classList.add("webconsole-msg-body-piece");
|
||||
statusNode.classList.add("webconsole-msg-status");
|
||||
linkNode.appendChild(statusNode);
|
||||
|
||||
let clipboardText = aActivityObject.method + " " + aActivityObject.url;
|
||||
|
||||
let messageNode = ConsoleUtils.createMessageNode(chromeDocument,
|
||||
|
|
|
@ -60,15 +60,13 @@ function onLoad(aEvent) {
|
|||
function testBasicNetLogging(aEvent) {
|
||||
browser.removeEventListener(aEvent.type, arguments.callee, true);
|
||||
|
||||
hudBox = HUDService.getHeadsUpDisplay(hudId);
|
||||
outputNode = HUDService.hudReferences[hudId].outputNode;
|
||||
|
||||
executeSoon(function() {
|
||||
let text = hudBox.querySelector(".hud-output-node").textContent;
|
||||
|
||||
isnot(text.indexOf("test-network.html"), -1, "found test-network.html");
|
||||
isnot(text.indexOf("testscript.js"), -1, "found testscript.js");
|
||||
isnot(text.indexOf("test-image.png"), -1, "found test-image.png");
|
||||
isnot(text.indexOf("network console"), -1, "found network console");
|
||||
findLogEntry("test-network.html");
|
||||
findLogEntry("testscript.js");
|
||||
findLogEntry("test-image.png");
|
||||
findLogEntry("network console");
|
||||
|
||||
finishTest();
|
||||
});
|
||||
|
|
|
@ -87,8 +87,7 @@ var consoleObserver = {
|
|||
"no duplicate for fooDuplicateError1");
|
||||
}
|
||||
|
||||
ok(text.indexOf("test-duplicate-error.html") > -1,
|
||||
"found test-duplicate-error.html");
|
||||
findLogEntry("test-duplicate-error.html");
|
||||
|
||||
finishTest();
|
||||
});
|
||||
|
|
|
@ -20,6 +20,7 @@
|
|||
*
|
||||
* Contributor(s):
|
||||
* Mihai Șucan <mihai.sucan@gmail.com>
|
||||
* Patrick Walton <pcwalton@mozilla.com>
|
||||
*
|
||||
* Alternatively, the contents of this file may be used under the terms of
|
||||
* either the GNU General Public License Version 2 or later (the "GPL"), or
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
* http://creativecommons.org/publicdomain/zero/1.0/
|
||||
*
|
||||
* Contributor(s):
|
||||
* Mihai Șucan <mihai.sucan@gmail.com>
|
||||
* Mihai ucan <mihai.sucan@gmail.com>
|
||||
*
|
||||
* ***** END LICENSE BLOCK ***** */
|
||||
|
||||
|
@ -27,7 +27,12 @@ function tabLoad1(aEvent) {
|
|||
browser.addEventListener("load", tabLoad2, true);
|
||||
|
||||
// Reload so we get some output in the console.
|
||||
browser.contentWindow.location.reload();
|
||||
// Wait a little to let the cache entry for test-console.html be released,
|
||||
// otherwise the network load wouldn't be logged in the console and test
|
||||
// would fail and stuck in tabLoad2. See bug 579846 comment 73.
|
||||
executeSoon(function() {
|
||||
browser.contentWindow.location.reload();
|
||||
});
|
||||
log(document);
|
||||
}
|
||||
|
||||
|
|
|
@ -18,41 +18,35 @@ function onContentLoaded()
|
|||
let HUD = HUDService.hudReferences[hudId];
|
||||
msgs = HUD.outputNode.querySelectorAll(".hud-msg-node");
|
||||
|
||||
ok(findEntry("hud-networkinfo", "test-bug-601177-log-levels.html"),
|
||||
"found test-bug-601177-log-levels.html");
|
||||
findEntry(HUD, "hud-networkinfo", "test-bug-601177-log-levels.html",
|
||||
"found test-bug-601177-log-levels.html");
|
||||
|
||||
ok(findEntry("hud-networkinfo", "test-bug-601177-log-levels.js"),
|
||||
"found test-bug-601177-log-levels.js");
|
||||
findEntry(HUD, "hud-networkinfo", "test-bug-601177-log-levels.js",
|
||||
"found test-bug-601177-log-levels.js");
|
||||
|
||||
ok(findEntry("hud-networkinfo", "test-image.png"),
|
||||
"found test-image.png");
|
||||
findEntry(HUD, "hud-networkinfo", "test-image.png", "found test-image.png");
|
||||
|
||||
ok(findEntry("hud-network", "foobar-known-to-fail.png"),
|
||||
"found foobar-known-to-fail.png");
|
||||
findEntry(HUD, "hud-network", "foobar-known-to-fail.png",
|
||||
"found foobar-known-to-fail.png");
|
||||
|
||||
ok(findEntry("hud-exception", "foobarBug601177exception"),
|
||||
"found exception");
|
||||
findEntry(HUD, "hud-exception", "foobarBug601177exception",
|
||||
"found exception");
|
||||
|
||||
ok(findEntry("hud-jswarn", "undefinedPropertyBug601177"),
|
||||
"found strict warning");
|
||||
findEntry(HUD, "hud-jswarn", "undefinedPropertyBug601177",
|
||||
"found strict warning");
|
||||
|
||||
ok(findEntry("hud-jswarn", "foobarBug601177strictError"),
|
||||
"found strict error");
|
||||
findEntry(HUD, "hud-jswarn", "foobarBug601177strictError",
|
||||
"found strict error");
|
||||
|
||||
msgs = null;
|
||||
Services.prefs.setBoolPref("javascript.options.strict", false);
|
||||
finishTest();
|
||||
}
|
||||
|
||||
function findEntry(aClass, aString)
|
||||
function findEntry(aHUD, aClass, aString, aMessage)
|
||||
{
|
||||
for (let i = 0, n = msgs.length; i < n; i++) {
|
||||
if (msgs[i].classList.contains(aClass) &&
|
||||
msgs[i].textContent.indexOf(aString) > -1) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
return testLogEntry(aHUD.outputNode, aString, aMessage, false, false,
|
||||
aClass);
|
||||
}
|
||||
|
||||
function test()
|
||||
|
|
|
@ -95,15 +95,20 @@ function addTab(aURL)
|
|||
* find only messages that are visible, not hidden by the filter.
|
||||
* @param {boolean} [aFailIfFound=false]
|
||||
* fail the test if the string is found in the output node.
|
||||
* @param {string} aClass [optional]
|
||||
* find only messages with the given CSS class.
|
||||
*/
|
||||
function testLogEntry(aOutputNode, aMatchString, aMsg, aOnlyVisible,
|
||||
aFailIfFound)
|
||||
aFailIfFound, aClass)
|
||||
{
|
||||
let selector = ".hud-msg-node";
|
||||
// Skip entries that are hidden by the filter.
|
||||
if (aOnlyVisible) {
|
||||
selector += ":not(.hud-filtered-by-type)";
|
||||
}
|
||||
if (aClass) {
|
||||
selector += "." + aClass;
|
||||
}
|
||||
|
||||
let msgs = aOutputNode.querySelectorAll(selector);
|
||||
let found = false;
|
||||
|
@ -113,10 +118,31 @@ function testLogEntry(aOutputNode, aMatchString, aMsg, aOnlyVisible,
|
|||
found = true;
|
||||
break;
|
||||
}
|
||||
|
||||
// Search the labels too.
|
||||
let labels = msgs[i].querySelectorAll("label");
|
||||
for (let j = 0; j < labels.length; j++) {
|
||||
if (labels[j].getAttribute("value").indexOf(aMatchString) > -1) {
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
is(found, !aFailIfFound, aMsg);
|
||||
}
|
||||
|
||||
/**
|
||||
* A convenience method to call testLogEntry().
|
||||
*
|
||||
* @param string aString
|
||||
* The string to find.
|
||||
*/
|
||||
function findLogEntry(aString)
|
||||
{
|
||||
testLogEntry(outputNode, aString, "found " + aString);
|
||||
}
|
||||
|
||||
function openConsole()
|
||||
{
|
||||
HUDService.activateHUDForContext(tab);
|
||||
|
|
|
@ -160,6 +160,7 @@
|
|||
</p>
|
||||
|
||||
<ul>
|
||||
<li><a href="about:license#angle">ANGLE License</a></li>
|
||||
<li><a href="about:license#apple">Apple License</a></li>
|
||||
<li><a href="about:license#apple-mozilla">Apple/Mozilla NPRuntime License</a></li>
|
||||
<li><a href="about:license#apple-torch">Apple/Torch Mobile License</a></li>
|
||||
|
@ -1946,7 +1947,48 @@ necessary. Here is a sample; alter the names:
|
|||
<p>That's all there is to it!
|
||||
|
||||
|
||||
<hr>
|
||||
|
||||
<h1><a name="angle"></a>ANGLE License</h1>
|
||||
|
||||
<p class="correctme">This license applies to files in the directory <span class="path">gfx/angle/</span>.</p>
|
||||
|
||||
<pre>
|
||||
Copyright (C) 2002-2010 The ANGLE Project Authors.
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions
|
||||
are met:
|
||||
|
||||
Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
|
||||
Redistributions in binary form must reproduce the above
|
||||
copyright notice, this list of conditions and the following
|
||||
disclaimer in the documentation and/or other materials provided
|
||||
with the distribution.
|
||||
|
||||
Neither the name of TransGaming Inc., Google Inc., 3DLabs Inc.
|
||||
Ltd., nor the names of their contributors may be used to endorse
|
||||
or promote products derived from this software without specific
|
||||
prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
|
||||
FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
|
||||
COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
||||
BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
|
||||
ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
POSSIBILITY OF SUCH DAMAGE.
|
||||
</pre>
|
||||
|
||||
|
||||
<hr>
|
||||
|
||||
<h1><a name="apple"></a>Apple License</h1>
|
||||
|
|
|
@ -364,11 +364,11 @@ menuitem.menuitem-non-iconic {
|
|||
%ifdef MOZ_WIDGET_GTK2
|
||||
/********* detection of system setting to use icons in menus ***********/
|
||||
@media not all and (-moz-images-in-menus) {
|
||||
menuitem:not([type]):not(.menuitem-with-favicon) > .menu-iconic-left {
|
||||
.menu-iconic-left {
|
||||
visibility: hidden;
|
||||
}
|
||||
menu > .menu-iconic-left {
|
||||
visibility: hidden;
|
||||
:-moz-any(menuitem[type], .menuitem-with-favicon) > .menu-iconic-left {
|
||||
visibility: visible;
|
||||
}
|
||||
}
|
||||
%endif
|
||||
|
|
|
@ -87,27 +87,6 @@ selectAllCmd.accesskey=A
|
|||
timestampFormat=%02S:%02S:%02S.%03S
|
||||
|
||||
helperFuncUnsupportedTypeError=Can't call pprint on this type of object.
|
||||
# LOCALIZATION NOTE (networkUrlWithStatus):
|
||||
#
|
||||
# When the HTTP request is started only the URL of the request is printed to the
|
||||
# WebConsole. As the response status of the HTTP request arrives, the URL string
|
||||
# is replaced by this string (the response status can look like `HTTP/1.1 200 OK`).
|
||||
# The bracket is not closed to mark that this request is not done by now. As the
|
||||
# request is finished (the HTTP connection is closed) this string is replaced
|
||||
# by `networkUrlWithStatusAndDuration` which has a closing the braket.
|
||||
#
|
||||
# %1$S = URL of network request
|
||||
# %2$S = response status code from the server (e.g. `HTTP/1.1 200 OK`)
|
||||
networkUrlWithStatus=%1$S [%2$S
|
||||
# LOCALIZATION NOTE (networkUrlWithStatusAndDuration):
|
||||
#
|
||||
# When the HTTP request is finished (the HTTP connection is closed) this string
|
||||
# replaces the former `networkUrlWithStatus` string in the WebConsole.
|
||||
#
|
||||
# %1$S = URL of network request
|
||||
# %2$S = response status code from the server (e.g. `HTTP/1.1 200 OK`)
|
||||
# %3$S = duration for the complete network request in milliseconds
|
||||
networkUrlWithStatusAndDuration=%1$S [%2$S %3$Sms]
|
||||
NetworkPanel.label=Inspect Network Request
|
||||
# LOCALIZATION NOTE (NetworkPanel.deltaDurationMS):
|
||||
#
|
||||
|
|
|
@ -541,6 +541,11 @@ endif # DMG
|
|||
endif # MOZ_PKG_MANIFEST
|
||||
endif # UNIVERSAL_BINARY
|
||||
$(OPTIMIZE_JARS_CMD) --optimize $(DIST)/jarlog/ $(DIST)/bin/chrome $(DIST)/$(STAGEPATH)$(MOZ_PKG_DIR)/chrome
|
||||
ifeq ($(USE_ELF_HACK)$(HOST_OS_ARCH)$(OS_ARCH)$(OS_TARGET),1LinuxLinuxLinux)
|
||||
ifneq (,$(filter %86 x86_64 arm,$(OS_TEST)))
|
||||
cd $(DIST)/$(STAGEPATH)$(MOZ_PKG_DIR); find . -name "*$(DLL_SUFFIX)" | xargs $(DEPTH)/build/unix/elfhack/elfhack
|
||||
endif
|
||||
endif
|
||||
ifndef PKG_SKIP_STRIP
|
||||
@echo "Stripping package directory..."
|
||||
@cd $(DIST)/$(STAGEPATH)$(MOZ_PKG_DIR); find . ! -type d \
|
||||
|
|
|
@ -44,7 +44,9 @@
|
|||
|
||||
/* ::::: menu/menuitem ::::: */
|
||||
|
||||
menu, menuitem {
|
||||
menu,
|
||||
menuitem,
|
||||
.splitmenu-menuitem {
|
||||
-moz-appearance: menuitem;
|
||||
-moz-box-align: center;
|
||||
max-width: 42em;
|
||||
|
@ -62,13 +64,15 @@ menuitem[default="true"] {
|
|||
}
|
||||
|
||||
menu[_moz-menuactive="true"],
|
||||
menuitem[_moz-menuactive="true"] {
|
||||
menuitem[_moz-menuactive="true"],
|
||||
.splitmenu-menuitem[_moz-menuactive="true"] {
|
||||
color: -moz-menuhovertext;
|
||||
background-color: -moz-menuhover;
|
||||
}
|
||||
|
||||
menu[disabled="true"],
|
||||
menuitem[disabled="true"] {
|
||||
menuitem[disabled="true"],
|
||||
.splitmenu-menuitem[disabled="true"] {
|
||||
color: GrayText;
|
||||
}
|
||||
|
||||
|
|
|
@ -91,6 +91,14 @@
|
|||
-moz-margin-end: 6px;
|
||||
}
|
||||
|
||||
.webconsole-msg-body-piece {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.webconsole-msg-url {
|
||||
margin: 0 6px;
|
||||
}
|
||||
|
||||
.webconsole-location {
|
||||
margin-top: 0;
|
||||
margin-bottom: 0;
|
||||
|
|
|
@ -94,6 +94,14 @@
|
|||
-moz-margin-end: 6px;
|
||||
}
|
||||
|
||||
.webconsole-msg-body-piece {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.webconsole-msg-url {
|
||||
margin: 0 6px;
|
||||
}
|
||||
|
||||
.webconsole-location {
|
||||
margin-top: 0;
|
||||
margin-bottom: 0;
|
||||
|
|
|
@ -29,7 +29,7 @@ wizard {
|
|||
|
||||
.wizard-header-box-icon {
|
||||
margin-top: 3px;
|
||||
-moz-margin-end: 20px
|
||||
-moz-margin-end: 20px;
|
||||
margin-bottom: 0;
|
||||
-moz-margin-start: 3px;
|
||||
}
|
||||
|
|
|
@ -45,7 +45,8 @@
|
|||
/* ::::: menu/menuitem ::::: */
|
||||
|
||||
menu,
|
||||
menuitem {
|
||||
menuitem,
|
||||
.splitmenu-menuitem {
|
||||
-moz-appearance: menuitem;
|
||||
-moz-box-align: center;
|
||||
color: MenuText;
|
||||
|
@ -60,8 +61,10 @@ menuitem[default="true"] {
|
|||
|
||||
menu[disabled="true"],
|
||||
menuitem[disabled="true"],
|
||||
.splitmenu-menuitem[disabled="true"],
|
||||
menu[_moz-menuactive="true"][disabled="true"],
|
||||
menuitem[_moz-menuactive="true"][disabled="true"] {
|
||||
menuitem[_moz-menuactive="true"][disabled="true"],
|
||||
.splitmenu-menuitem[_moz-menuactive="true"][disabled="true"] {
|
||||
color: GrayText;
|
||||
text-shadow: none;
|
||||
}
|
||||
|
@ -69,7 +72,8 @@ menuitem[_moz-menuactive="true"][disabled="true"] {
|
|||
@media all and (-moz-windows-classic) {
|
||||
menu[disabled="true"],
|
||||
menubar > menu[disabled="true"][_moz-menuactive="true"],
|
||||
menuitem[disabled="true"] {
|
||||
menuitem[disabled="true"],
|
||||
.splitmenu-menuitem[disabled="true"] {
|
||||
color: ThreeDShadow;
|
||||
text-shadow: 1px 1px ThreeDHighlight;
|
||||
}
|
||||
|
@ -125,7 +129,8 @@ menuitem.spell-suggestion {
|
|||
}
|
||||
|
||||
menu.menu-iconic > .menu-iconic-left,
|
||||
menuitem.menuitem-iconic > .menu-iconic-left {
|
||||
menuitem.menuitem-iconic > .menu-iconic-left,
|
||||
.splitmenu-menuitem[iconic="true"] > .menu-iconic-left {
|
||||
-moz-appearance: menuimage;
|
||||
padding-top: 2px;
|
||||
}
|
||||
|
@ -194,7 +199,8 @@ menupopup > menuitem {
|
|||
}
|
||||
|
||||
menu[_moz-menuactive="true"],
|
||||
menuitem[_moz-menuactive="true"] {
|
||||
menuitem[_moz-menuactive="true"],
|
||||
.splitmenu-menuitem[_moz-menuactive="true"] {
|
||||
background-color: -moz-menuhover;
|
||||
color: -moz-menuhovertext;
|
||||
}
|
||||
|
|
|
@ -90,6 +90,14 @@
|
|||
-moz-margin-end: 6px;
|
||||
}
|
||||
|
||||
.webconsole-msg-body-piece {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.webconsole-msg-url {
|
||||
margin: 0 6px;
|
||||
}
|
||||
|
||||
.webconsole-location {
|
||||
margin-top: 0;
|
||||
margin-bottom: 0;
|
||||
|
|
|
@ -194,8 +194,8 @@ NS_IMPL_ISUPPORTS1(nsFilePicker, nsIFilePicker)
|
|||
|
||||
nsFilePicker::nsFilePicker()
|
||||
: mMode(nsIFilePicker::modeOpen),
|
||||
mAllowURLs(PR_FALSE),
|
||||
mSelectedType(0)
|
||||
mSelectedType(0),
|
||||
mAllowURLs(PR_FALSE)
|
||||
{
|
||||
}
|
||||
|
||||
|
|
|
@ -6269,13 +6269,15 @@ get_inner_gdk_window (GdkWindow *aWindow,
|
|||
GList * child = g_list_nth(children, num - i - 1) ;
|
||||
if (child) {
|
||||
GdkWindow * childWindow = (GdkWindow *) child->data;
|
||||
gdk_window_get_geometry (childWindow, &cx, &cy, &cw, &ch, &cd);
|
||||
if ((cx < x) && (x < (cx + cw)) &&
|
||||
(cy < y) && (y < (cy + ch)) &&
|
||||
gdk_window_is_visible (childWindow)) {
|
||||
return get_inner_gdk_window (childWindow,
|
||||
x - cx, y - cy,
|
||||
retx, rety);
|
||||
if (get_window_for_gdk_window(childWindow)) {
|
||||
gdk_window_get_geometry (childWindow, &cx, &cy, &cw, &ch, &cd);
|
||||
if ((cx < x) && (x < (cx + cw)) &&
|
||||
(cy < y) && (y < (cy + ch)) &&
|
||||
gdk_window_is_visible (childWindow)) {
|
||||
return get_inner_gdk_window (childWindow,
|
||||
x - cx, y - cy,
|
||||
retx, rety);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -37,6 +37,7 @@
|
|||
* ***** END LICENSE BLOCK ***** */
|
||||
|
||||
#include <windows.h>
|
||||
#include <setupapi.h>
|
||||
#include "gfxWindowsPlatform.h"
|
||||
#include "GfxInfo.h"
|
||||
#include "GfxInfoWebGL.h"
|
||||
|
@ -180,6 +181,31 @@ static void normalizeDriverId(nsString& driverid) {
|
|||
}
|
||||
}
|
||||
|
||||
// Setup API functions
|
||||
typedef HDEVINFO (WINAPI*SetupDiGetClassDevsWFunc)(
|
||||
CONST GUID *ClassGuid,
|
||||
PCWSTR Enumerator,
|
||||
HWND hwndParent,
|
||||
DWORD Flags
|
||||
);
|
||||
typedef BOOL (WINAPI*SetupDiEnumDeviceInfoFunc)(
|
||||
HDEVINFO DeviceInfoSet,
|
||||
DWORD MemberIndex,
|
||||
PSP_DEVINFO_DATA DeviceInfoData
|
||||
);
|
||||
typedef BOOL (WINAPI*SetupDiGetDeviceRegistryPropertyWFunc)(
|
||||
HDEVINFO DeviceInfoSet,
|
||||
PSP_DEVINFO_DATA DeviceInfoData,
|
||||
DWORD Property,
|
||||
PDWORD PropertyRegDataType,
|
||||
PBYTE PropertyBuffer,
|
||||
DWORD PropertyBufferSize,
|
||||
PDWORD RequiredSize
|
||||
);
|
||||
typedef BOOL (WINAPI*SetupDiDestroyDeviceInfoListFunc)(
|
||||
HDEVINFO DeviceInfoSet
|
||||
);
|
||||
|
||||
|
||||
|
||||
/* Other interesting places for info:
|
||||
|
@ -232,50 +258,73 @@ GfxInfo::Init()
|
|||
mDeviceString = displayDevice.DeviceString;
|
||||
|
||||
|
||||
HKEY key, subkey;
|
||||
LONG result, enumresult;
|
||||
DWORD index = 0;
|
||||
WCHAR subkeyname[64];
|
||||
WCHAR value[128];
|
||||
DWORD dwcbData = sizeof(subkeyname);
|
||||
HMODULE setupapi = LoadLibraryW(L"setupapi.dll");
|
||||
|
||||
// "{4D36E968-E325-11CE-BFC1-08002BE10318}" is the display class
|
||||
result = RegOpenKeyExW(HKEY_LOCAL_MACHINE,
|
||||
L"System\\CurrentControlSet\\Control\\Class\\{4D36E968-E325-11CE-BFC1-08002BE10318}",
|
||||
0, KEY_QUERY_VALUE | KEY_ENUMERATE_SUB_KEYS, &key);
|
||||
if (result != ERROR_SUCCESS) {
|
||||
return rv;
|
||||
}
|
||||
if (setupapi) {
|
||||
SetupDiGetClassDevsWFunc setupGetClassDevs = (SetupDiGetClassDevsWFunc)
|
||||
GetProcAddress(setupapi, "SetupDiGetClassDevsW");
|
||||
SetupDiEnumDeviceInfoFunc setupEnumDeviceInfo = (SetupDiEnumDeviceInfoFunc)
|
||||
GetProcAddress(setupapi, "SetupDiEnumDeviceInfo");
|
||||
SetupDiGetDeviceRegistryPropertyWFunc setupGetDeviceRegistryProperty = (SetupDiGetDeviceRegistryPropertyWFunc)
|
||||
GetProcAddress(setupapi, "SetupDiGetDeviceRegistryPropertyW");
|
||||
SetupDiDestroyDeviceInfoListFunc setupDestroyDeviceInfoList = (SetupDiDestroyDeviceInfoListFunc)
|
||||
GetProcAddress(setupapi, "SetupDiDestroyDeviceInfoList");
|
||||
|
||||
nsAutoString wantedDriverId(mDeviceID);
|
||||
normalizeDriverId(wantedDriverId);
|
||||
if (setupGetClassDevs &&
|
||||
setupEnumDeviceInfo &&
|
||||
setupGetDeviceRegistryProperty &&
|
||||
setupDestroyDeviceInfoList) {
|
||||
/* create a device information set composed of the current display device */
|
||||
HDEVINFO devinfo = setupGetClassDevs(NULL,
|
||||
PromiseFlatString(mDeviceID).get(),
|
||||
NULL,
|
||||
DIGCF_PRESENT | DIGCF_PROFILE | DIGCF_ALLCLASSES);
|
||||
|
||||
while ((enumresult = RegEnumKeyExW(key, index, subkeyname, &dwcbData, NULL, NULL, NULL, NULL)) != ERROR_NO_MORE_ITEMS) {
|
||||
result = RegOpenKeyExW(key, subkeyname, 0, KEY_QUERY_VALUE, &subkey);
|
||||
if (result == ERROR_SUCCESS) {
|
||||
dwcbData = sizeof(value);
|
||||
result = RegQueryValueExW(subkey, L"MatchingDeviceId", NULL, NULL, (LPBYTE)value, &dwcbData);
|
||||
if (result == ERROR_SUCCESS) {
|
||||
nsAutoString matchingDeviceId(value);
|
||||
normalizeDriverId(matchingDeviceId);
|
||||
if (wantedDriverId.Find(matchingDeviceId) > -1) {
|
||||
/* we've found the driver we're looking for */
|
||||
result = RegQueryValueExW(subkey, L"DriverVersion", NULL, NULL, (LPBYTE)value, &dwcbData);
|
||||
if (result == ERROR_SUCCESS)
|
||||
mDriverVersion = value;
|
||||
result = RegQueryValueExW(subkey, L"DriverDate", NULL, NULL, (LPBYTE)value, &dwcbData);
|
||||
if (result == ERROR_SUCCESS)
|
||||
mDriverDate = value;
|
||||
break;
|
||||
if (devinfo != INVALID_HANDLE_VALUE) {
|
||||
HKEY key;
|
||||
LONG result;
|
||||
WCHAR value[255];
|
||||
DWORD dwcbData;
|
||||
SP_DEVINFO_DATA devinfoData;
|
||||
DWORD memberIndex = 0;
|
||||
|
||||
devinfoData.cbSize = sizeof(devinfoData);
|
||||
NS_NAMED_LITERAL_STRING(driverKeyPre, "System\\CurrentControlSet\\Control\\Class\\");
|
||||
/* enumerate device information elements in the device information set */
|
||||
while (setupEnumDeviceInfo(devinfo, memberIndex++, &devinfoData)) {
|
||||
/* get a string that identifies the device's driver key */
|
||||
if (setupGetDeviceRegistryProperty(devinfo,
|
||||
&devinfoData,
|
||||
SPDRP_DRIVER,
|
||||
NULL,
|
||||
(PBYTE)value,
|
||||
sizeof(value),
|
||||
NULL)) {
|
||||
nsAutoString driverKey(driverKeyPre);
|
||||
driverKey += value;
|
||||
result = RegOpenKeyExW(HKEY_LOCAL_MACHINE, driverKey.BeginReading(), 0, KEY_QUERY_VALUE, &key);
|
||||
if (result == ERROR_SUCCESS) {
|
||||
/* we've found the driver we're looking for */
|
||||
dwcbData = sizeof(value);
|
||||
result = RegQueryValueExW(key, L"DriverVersion", NULL, NULL, (LPBYTE)value, &dwcbData);
|
||||
if (result == ERROR_SUCCESS)
|
||||
mDriverVersion = value;
|
||||
dwcbData = sizeof(value);
|
||||
result = RegQueryValueExW(key, L"DriverDate", NULL, NULL, (LPBYTE)value, &dwcbData);
|
||||
if (result == ERROR_SUCCESS)
|
||||
mDriverDate = value;
|
||||
RegCloseKey(key);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
RegCloseKey(subkey);
|
||||
}
|
||||
index++;
|
||||
dwcbData = sizeof(subkeyname);
|
||||
}
|
||||
|
||||
RegCloseKey(key);
|
||||
setupDestroyDeviceInfoList(devinfo);
|
||||
}
|
||||
}
|
||||
|
||||
FreeLibrary(setupapi);
|
||||
}
|
||||
|
||||
const char *spoofedDriverVersionString = PR_GetEnv("MOZ_GFX_SPOOF_DRIVER_VERSION");
|
||||
if (spoofedDriverVersionString) {
|
||||
|
|
Загрузка…
Ссылка в новой задаче