Merge mozilla-central into Places.

This commit is contained in:
Shawn Wilsher 2011-01-24 14:09:52 -08:00
Родитель 92b5b0cad4 2fb3fcc38a
Коммит 045565c2b6
89 изменённых файлов: 4071 добавлений и 642 удалений

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

@ -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>
<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;
tabs.forEach(function (tab, index) {
if (!indices)
indices = tabs.map(function (tab) tab._tPos);
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;
}
// 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);
// 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;
}
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'>&nbsp;</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">

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

@ -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;

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

@ -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

28
build/unix/elfhack/README Normal file
Просмотреть файл

@ -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;
}

822
build/unix/elfhack/elf.cpp Normal file
Просмотреть файл

@ -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;
}

582
build/unix/elfhack/elfxx.h Normal file
Просмотреть файл

@ -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
}

148
build/unix/elfhack/test.c Normal file
Просмотреть файл

@ -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

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

@ -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) {
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*>(
static_cast<nsGlobalChromeWindow*>(
this)->mMessageManager.get())->Disconnect();
asChrome->mMessageManager.get())->Disconnect();
}
}
mInnerWindowHolder = nsnull;
@ -2075,10 +2079,10 @@ nsGlobalWindow::SetNewDocument(nsIDocument* aDocument,
}
JS_SetParent(cx, mJSObject, newInnerWindow->mJSObject);
}
mContext->SetOuterObject(mJSObject);
}
}
JSAutoEnterCompartment ac;
if (!ac.enter(cx, mJSObject)) {
@ -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,&lt;input id=fra&gt;
<iframe id="ifa" width="40" height="60" style="-moz-user-focus: ignore;" type="content"
src="data:text/html,&lt;input id=fra size='2'&gt;&lt;input id='fra-b' size='2'&gt;
&lt;iframe src='data:text/html,&lt;input id=frc&gt;&lt;iframe src=&quot;data:text/html,&lt;input id=frd&gt;&quot;&gt;&lt;/iframe&gt;'&gt;&lt;/iframe&gt;"/>
<iframe id="ifb" width="20" height="20" style="-moz-user-focus: ignore;"
src="data:text/html,&lt;input id=frd&gt;&lt;/iframe&gt;"/>

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

@ -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);

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

@ -56,8 +56,8 @@ Features to add:
<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; }
@ -69,6 +69,19 @@ Features to add:
#itemlist > table > tbody > tr.fail > td.url { background: red; }
*/
#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[
@ -79,6 +92,14 @@ 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);
@ -87,6 +108,47 @@ function ID(id) {
function load() {
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) {
@ -226,6 +288,21 @@ function build_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");
@ -249,6 +326,9 @@ function show_images(i) {
}
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) {
@ -265,6 +345,90 @@ function show_differences(cb) {
ID("diffrect").style.display = cb.checked ? "" : "none";
}
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>
@ -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="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>
@ -353,9 +544,11 @@ function show_differences(cb) {
</feMerge>
</filter>
</defs>
<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" />
<rect id="diffrect" filter="url(#showDifferences)" x="0" y="0" width="100%" height="100%" />
</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

Двоичный файл не отображается.

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

@ -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

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

@ -190,6 +190,26 @@ public:
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,12 +300,28 @@ HttpChannelParent::RecvCancel(const nsresult& status)
bool
HttpChannelParent::RecvSetCacheTokenCachedCharset(const nsCString& charset)
{
if (mCacheDescriptor)
mCacheDescriptor->SetMetaDataElement("charset",
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;
}
bool
HttpChannelParent::RecvUpdateAssociatedContentSecurity(const PRInt32& high,
const PRInt32& low,
@ -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;
@ -346,6 +354,11 @@ private:
// 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;
// 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.
// 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,25 +18,24 @@ function onContentLoaded()
let HUD = HUDService.hudReferences[hudId];
msgs = HUD.outputNode.querySelectorAll(".hud-msg-node");
ok(findEntry("hud-networkinfo", "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"),
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"),
findEntry(HUD, "hud-network", "foobar-known-to-fail.png",
"found foobar-known-to-fail.png");
ok(findEntry("hud-exception", "foobarBug601177exception"),
findEntry(HUD, "hud-exception", "foobarBug601177exception",
"found exception");
ok(findEntry("hud-jswarn", "undefinedPropertyBug601177"),
findEntry(HUD, "hud-jswarn", "undefinedPropertyBug601177",
"found strict warning");
ok(findEntry("hud-jswarn", "foobarBug601177strictError"),
findEntry(HUD, "hud-jswarn", "foobarBug601177strictError",
"found strict error");
msgs = null;
@ -44,15 +43,10 @@ function onContentLoaded()
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,6 +1947,47 @@ 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>

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

@ -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,6 +6269,7 @@ get_inner_gdk_window (GdkWindow *aWindow,
GList * child = g_list_nth(children, num - i - 1) ;
if (child) {
GdkWindow * childWindow = (GdkWindow *) child->data;
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)) &&
@ -6279,6 +6280,7 @@ get_inner_gdk_window (GdkWindow *aWindow,
}
}
}
}
*retx = x;
*rety = y;
return aWindow;

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

@ -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 (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) {
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);
dwcbData = sizeof(value);
result = RegQueryValueExW(key, L"DriverVersion", NULL, NULL, (LPBYTE)value, &dwcbData);
if (result == ERROR_SUCCESS)
mDriverVersion = value;
result = RegQueryValueExW(subkey, L"DriverDate", NULL, NULL, (LPBYTE)value, &dwcbData);
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) {