Obiekt XPCNativeWrapper
służy do opakowania obiektu, tak aby można było uzyskać do niego dostęp w bezpieczny sposób z poziomu kodu uprzywilejowanego. Można z niego korzystać we wszystkich wersjach Firefoksa, przy czym jego działanie zostało nieco zmienione w wersji 1.5 (Gecko 1.8) i nowszych. Informacje na temat działania obiektu XPCNativeWrapper
w wersjach Firefoksa starszych niż 1.5 można znaleźć w artykule XPCNativeWrapper
w bazie wiedzy witryny MozillaZine. W niniejszym dokumencie opisano działanie obiektu XPCNativeWrapper
w programie Firefox w wersji 1.5 i nowszych.
Jak działa obiekt XPCNativeWrapper
Obiekt XPCNativeWrapper
ogranicza dostęp do własności i metod obiektu, który jest przez niego opakowany. Za pośrednictwem obiektu XPCNativeWrapper
dostępne są tylko te własności i metody, które zdefiniowano w specyfikacji IDL (Interface Definition Language, język definicji interfejsów) lub w specyfikacji DOM Level 0 (przy czym niektóre własności i metody DOM Level 0 nie działają w odniesieniu do obiektu XPCNativeWrapper
). Obiekt XPCNativeWrapper
nie udostępnia przede wszystkim własności dodanych do opakowanego obiektu za pomocą kodu JavaScript ani metod pobierania i ustawiania zdefiniowanych za pomocą metod __defineGetter__
oraz __defineSetter__
. W rezultacie możliwy jest bezpieczny dostęp do metod obiektu zdefiniowanych w specyfikacji IDL.
Należy koniecznie zapoznać się z sekcją Znane błędy, zwłaszcza w przypadku pisania kodu przeznaczonego do działania w wydaniach Firefoksa z serii 1.5.0.x.
Typy obiektu XPCNativeWrapper
W programie Firefox 1.5 istnieją trzy typy obiektu XPCNativeWrapper
. Wszystkie służą do opakowania potencjalnie niezabezpieczonych obiektów i umożliwiają bezpieczny dostęp do własności i metod tych obiektów.
O różnicach w działaniu między trzema typami obiektu XPCNativeWrapper
decydują dwie cechy, jakie może mieć obiekt opakowujący XPCNativeWrapper
. Obiekt XPCNativeWrapper
może być jawny (lub odwrotnie: niejawny) albo też głęboki (lub odwrotnie: płytki). Typ tworzonego obiektu opakowującego jest określany przez sposób jego utworzenia:
Utworzony przez | Jawny/niejawny | Głęboki/płytki |
---|---|---|
Chroniony skrypt uzyskujący dostęp do niezaufanego obiektu | Niejawny | Głęboki |
Konstruktor wywołany z argumentami typu String
| Jawny | Płytki |
Konstruktor wywołany bez argumentów typu String
| Jawny | Głęboki |
Obiekty jawne (explicit) i niejawne (implicit)
Różnica w działaniu jawnych i niejawnych obiektów XPCNativeWrapper
polega na dostępie do własności opakowanego obiektu: w przypadku obiektów niejawnych dostęp z poziomu skryptu, który nie jest chroniony, NIE jest bezpieczny. Żądania dostępu do własności opakowanego obiektu są wówczas przekazywane do własności wrappedJSObject
obiektu XPCNativeWrapper
.
Oznacza to, że w skryptach, które nie są chronione, nie trzeba zajmować się możliwością wystąpienia błędów, ponieważ do skryptów tych przekazywane są niejawne obiekty XPCNativeWrapper
. Z drugiej strony w skryptach tego rodzaju należy przewidzieć możliwość niezabezpieczonego dostępu do obiektu.
Dostęp do własności jawnego obiektu XPCNativeWrapper
jest zawsze bezpieczny, niezależnie od tego, czy wywołujący skrypt jest chroniony.
Obiekty głębokie (deep) i płytkie (shallow)
Różnica w działaniu głębokich i płytkich obiektów XPCNativeWrapper
jest następująca: przy próbie dostępu do własności głębokiego obiektu opakowującego lub wywołaniu funkcji tego obiektu zwracana wartość jest również opakowana przez obiekt XPCNativeWrapper
. Ten nowy obiekt XPCNativeWrapper
jest także obiektem głębokim. Jest również jawny tylko jeżeli obiekt XPCNativeWrapper
, do którego własności uzyskiwano dostęp, jest obiektem jawnym. Natomiast przy próbie dostępu do własności płytkiego obiektu opakowującego lub wywołaniu funkcji tego obiektu zwracana wartość może być obiektem niezabezpieczonym.
Załóżmy, że dla tego samego obiektu window
istnieją trzy instancje obiektów XPCNativeWrapper
. Nazwijmy je deepExplicitWindow
, deepImplicitWindow
i shallowWindow
:
var doc1 = deepExplicitWindow.document; // Zmienna doc1 jest teraz głębokim jawnym obiektem XPCNativeWrapper // opakowującym obiekt document. Wywołanie na przykład metody // doc1.open() jest bezpieczne.
var doc2 = deepImplicitWindow.document; // Jeżeli w skrypcie wywołującym ustawiono parametr xpcnativewrappers=yes, // zmienna doc2 jest teraz głębokim niejawnym obiektem XPCNativeWrapper // opakowującym obiekt document. // W przeciwnym przypadku zmienna doc2 jest teraz niezabezpieczonym obiektem // document, ponieważ żądanie dostępu do własności zostało po prostu // przekazane do niezabezpieczonego obiektu window.
var doc3 = shallowWindow.document; // Zmienna doc3 jest teraz niezabezpieczonym obiektem document.
Tworzenie obiektów XPCNativeWrapper
Istnieją trzy różne sposoby tworzenia obiektów XPCNativeWrapper
— po jednym dla każdego z trzech typów.
Dostęp do niezaufanego obiektu z poziomu chronionego skryptu
Za każdym razem, kiedy skrypt chroniony uzyskuje dostęp do obiektu niezaufanego, zwracany jest niejawny głęboki obiekt XPCNativeWrapper
. Dostęp do własności takiego obiektu XPCNativeWrapper
z poziomu skryptu chronionego jest bezpieczny.
Utworzony w ten sposób obiekt opakowujący pozostaje dostępny, dopóki istnieje opakowywany obiekt. Przy dwukrotnej próbie dostępu do obiektu w jednym wierszu kodu zwrócony zostanie ten sam obiekt XPCNativeWrapper
.
Co to jest skrypt chroniony?
W wersjach programu Firefox od 1.5 do 1.5.0.5 ochrona skryptu lub jej brak zależy wyłącznie od identyfikatora URI skryptu. Skrypt jest chroniony tylko jeżeli jego identyfikator URI rozpoczyna się prefiksem zdefiniowanym jako chroniony; skrypty ładowane w inny sposób niż przez identyfikator URI (np. komponenty implementowane za pomocą kodu JavaScript) nie są chronione. Prefiksy chronione w programie Firefox są określone w Rejestrze Chrome.
Domyślnie wszystkie pakiety składników oprogramowania (content) są chronione. Oznacza to, że każdy identyfikator URI rozpoczynający się ciągiem "<tt>chrome://<nazwa pakietu>/content/</tt>" (niezależnie od pakietu) jest chroniony. W poszczególnych pakietach można zmienić tę konfigurację, ustawiając odpowiedni znacznik w pliku manifestu chrome.
Począwszy od wersji 1.5.0.6 Firefoksa, komponenty zaimplementowane za pomocą kodu JavaScript są skryptami chronionymi. Skrypt jest więc chroniony zarówno wówczas, gdy jest załadowany przez identyfikator URI rozpoczynający się chronionym prefiksem, jak i wtedy, gdy jest komponentem zaimplementowanym za pomocą kodu JavaScript.
Co to jest obiekt niezaufany?
Każdy obiekt może być zaufany lub niezaufany. Obiekt jest zaufany, gdy spełniony jest przynajmniej jeden z poniższych warunków:
- Jego rodzic (własność
__parent__
w języku JavaScript) jest obiektem zaufanym. - Jest to obiekt zakresu nadrzędnego w komponencie JavaScript.
- Jest to obiekt
window
okna zaufanego.
Ponieważ w przypadku wszystkich obiektów DOM w oknie ich własność __parent__
zawierać będzie obiekt window
tego okna, będą one zaufane jedynie wówczas, gdy okno, w którym się znajdują, jest zaufane.
Co to jest okno zaufane?
Zaufanie do okna zależne jest od zawierającego je kontenera. Okno jest zaufane, gdy spełniony jest przynajmniej jeden z poniższych warunków:
- Jest to okno najwyższego poziomu (np.
<xul:window>
,<xul:dialog>
lub identyfikator URI przekazany do argumentu <tt>-chrome</tt> wiersza polecenia). - Rodzic okna jest zaufany oraz spełniony jest jeden z trzech warunków:
- Okno nie jest załadowane w obiekcie
<xul:iframe>
ani<xul:browser>
. - Ładujący okno obiekt
<xul:iframe>
lub<xul:browser>
nie ma atrybutu "type". - Wartość atrybutu "type" obiektu
<xul:iframe>
lub<xul:browser>
ładującego okno nie jest równa "content" ani nie rozpoczyna się od ciągu znaków "content-".
- Okno nie jest załadowane w obiekcie
Uwaga: to, czy okno jest zaufane, nie zależy od identyfikatora URI załadowanego w tym oknie. Dlatego jeżeli kod z poniższego przykładu zostanie wykonany w dokumencie, którego okno jest już zaufane, zostaną utworzone okna zaufane:
-
<xul:browser>
-
<xul:browser type="chrome">
-
<xul:browser type="wsciekly_pies">
-
<xul:iframe type="costam">
-
<html:iframe>
-
<html:iframe type="content">
Z kolei wykonanie poniższego kodu nie spowoduje utworzenia okien zaufanych:
-
<xul:browser type="content">
-
<xul:iframe type="content-primary">
Należy także zauważyć, że każde okno potomne okna niezaufanego automatycznie staje się niezaufane.
Co dzieje się, gdy skrypt uzyskuje dostęp do obiektu?
W poniższej tabeli opisano, co dzieje się, gdy skrypt uzyskuje dostęp do obiektu i jaka jest rola obiektu opakowującego.
Skrypt | Obiekt | Rezultaty |
---|---|---|
Chroniony | Zaufany | Nie jest tworzony obiekt opakowujący, w wyniku czego skrypt uzyskuje pełny dostęp do obiektu. |
Chroniony | Niezaufany | Tworzony jest niejawny głęboki obiekt XPCNativeWrapper .
|
Niechroniony | Zaufany | Nie jest tworzony obiekt opakowujący, tak jak w pierwszym przypadku (skrypt chroniony i obiekt zaufany). |
Niechroniony | Niezaufany | Nie jest tworzony obiekt opakowujący, tak jak w pierwszym przypadku (skrypt chroniony i obiekt zaufany) |
Korzystanie z konstruktora obiektu XPCNativeWrapper
XPCNativeWrapper
są tworzone automatycznie. Nie ma potrzeby używania konstruktora obiektu XPCNativeWrapper
, chyba że tworzony kod ma działać w starszych wersjach przeglądarki lub wyłączono obsługę obiektów XPCNativeWrapper
. Wywołanie konstruktora obiektu XPCNativeWrapper
z argumentami typu String
Rozważmy przykład:
var contentWinWrapper = new XPCNativeWrapper(content, "document");
Tworzony jest niejawny płytki obiekt XPCNativeWrapper
. Składnia została utrzymana ze względu na zgodność z wersjami Firefoksa wcześniejszymi niż 1.5. O ile do wszystkich własności obiektu contentWinWrapper
można teraz uzyskać dostęp w bezpieczny sposób, o tyle dostęp do zwracanych przez nie wartości NIE jest bezpieczny (podobnie jak w wersjach Firefoksa wcześniejszych niż 1.5), ponieważ obiekt XPCNativeWrapper
jest płytki. Aby zatem porównać tytuł dokumentu zawartości (content) z bieżącym zaznaczeniem zawartości, należy wykonać następujący kod:
var winWrapper = new XPCNativeWrapper(content, "document", "getSelection()"); var docWrapper = new XPCNativeWrapper(winWrapper.document, "title"); return docWrapper.title == winWrapper.getSelection();
podobnie jak w wersjach Firefoksa starszych niż 1.5. Należy zwrócić uwagę, że argument "getSelection()"
nie jest niezbędny; jeżeli kod nie jest przeznaczony do użytku w wersjach Firefoksa starszych niż 1.5, można go usunąć. Jedynym elementem wymaganym do utworzenia tego typu obiektu XPCNativeWrapper
w programie Firefox 1.5 lub nowszym jest pojedynczy argument typu String
, umieszczony po opakowywanym obiekcie.
Wywołanie konstruktora obiektu XPCNativeWrapper
bez argumentów typu String
Rozważmy przykład:
var contentWinWrapper = new XPCNativeWrapper(content);
Tworzony jest jawny głęboki obiekt XPCNativeWrapper
. Dostęp do własności tego obiektu XPCNativeWrapper
jest bezpieczny, a zwracane wartości będą także opakowane przez jawne głębokie obiekty XPCNativeWrapper
.
Czas istnienia obiektu XPCNativeWrapper
Jawne obiekty XPCNativeWrapper
istnieją dopóki, dopóty istnieją do nich odwołania. Utworzenie nowego jawnego obiektu XPCNativeWrapper
dla tego samego potencjalnie niezabezpieczonego obiektu object spowoduje utworzenie nowego obiektu opakowującego; należy mieć to na uwadze przy ustawianiu własności „expando”.
Niejawne obiekty XPCNativeWrapper
istnieją tak długo, jak opakowywane przez nie obiekty.
Ustawianie własności „expando” obiektu XPCNativeWrapper
Możliwe jest ustawienie własności „expando” (tj. własności o nazwach, które nie mają odpowiedników wśród własności zdefiniowanych w specyfikacji IDL) dla obiektów XPCNativeWrapper
. Własności te będą widoczne z poziomu chrome, ale nie będzie można uzyskać do nich dostępu z poziomu zawartości (content). Nie istnieje bezpieczny sposób ustawienia własności „expando” z poziomu chrome i odczytania jej następnie z poziomu zawartości.
Niezabezpieczony dostęp do własności
Jeżeli z jakiegoś powodu wymagany jest niezabezpieczony dostęp do własności, można go uzyskać za pomocą własności wrappedJSObject
obiektu opakowującego. Jeżeli na przykład obiekt docWrapper
jest obiektem opakowującym obiekt doc
, to własność
docWrapper.wrappedJSObject.prop
jest identyczna z własnością
doc.prop
Jak podkreślono w tytule niniejszej sekcji, technika ta nie jest bezpieczna. Własności wrappedJSObject
nie należy używać w kodzie produkcyjnym w celu ominięcia pośrednictwa obiektu XPCNativeWrapper.
W programie Firefox 3 własność wrappedJSObject
zwraca kolejny obiekt opakowujący obiektu zawartości JavaScript (XPCSafeJSObjectWrapper), co pozwala na bezpieczne sprawdzenie obiektu zawartości — patrz Obiekty opakowujące XPConnect#XPCSafeJSObjectWrapper.
Informacje o lepszych rozwiązaniach alternatywnych znajdują się w artykule Interakcja pomiędzy stronami uprzywilejowanymi i stronami bez przywilejów.
Znane błędy
W wersjach z serii 1.5.0.x występują dwa znane błędy w obsłudze obiektów XPCNativeWrapper
:
- W wersjach Firefoksa od 1.5 do 1.5.0.4 występuje błąd 337095, w wyniku którego w niektórych przypadkach dla skryptów chronionych nie są tworzone obiekty opakowujące. Z reguły, gdy skrypt chroniony uzyskuje dostęp do własności lub wywołuje funkcję, a ta własność lub funkcja zwraca obiekt niezaufany, tworzony jest obiekt opakowujący. Jeżeli jednak funkcja w skrypcie chronionym jest wywoływana z poziomu kodu w języku C++, a jako argument jest do niej przekazywany obiekt niezaufany, obiekt opakowujący nie zostanie utworzony. W funkcjach, które mają być wywołane w taki sposób, należy zaimplementować własną technikę opakowania. Błąd ten naprawiono w Firefoksie w wersji 1.5.0.5 i nowszych.
- W wersjach Firefoksa od 1.5 do 1.5.0.5 występuje błąd 345991, w wyniku którego komponenty utworzone przy użyciu języka JavaScript nie są skryptami chronionymi. Błąd ten naprawiono w Firefoksie w wersji 1.5.0.6 i nowszych.
Ograniczenia w korzystaniu z obiektu XPCNativeWrapper
Niektóre często stosowane własności i style kodowania nie mogą być używane z obiektami XPCNativeWrapper
:
- Przypisywanie wartości lub odczytywanie własności
on*
obiektuXPCNativeWrapper
opakowującego węzeł DOM lub obiektwindow
spowoduje zgłoszenie wyjątku. (Zamiast tej techniki należy użyć metodyaddEventListener
oraz — jeżeli używana była dyrektywareturn false;
— należy zastąpić ją metodąevent.preventDefault()
w kodzie obserwatora). - Nie jest możliwy dostęp do ramek określanych przy użyciu nazwy okna (np.
window.nazwaRamki
) w odniesieniu do obiektówXPCNativeWrapper
. - Nie jest możliwy dostęp do własności
document.all
w odniesieniu do obiektuXPCNativeWrapper
opakowującego obiektdocument
. - Nie jest możliwy dostęp do nazwanych elementów określanych przy użyciu nazw w odniesieniu do obiektu
XPCNativeWrapper
opakowującego dokument HTML. Jeżeli na przykład istnieje element<form name="foo">
, a obiektdocWrapper
jest obiektem opakowującym dokument HTMLdoc
, to własnośćdoc.foo
zwraca obiektHTMLFormElement
, ale własnośćdocWrapper.foo
zwraca wartośćundefined
. Aby uzyskać dostęp do nazwanych elementów, należy użyć metodydocWrapper.forms.namedItem("foo")
. - Nie jest możliwy dostęp do elementów określanych przy użyciu identyfikatorów w odniesieniu do obiektu
XPCNativeWrapper
opakowującego dokument HTML. Należy użyć metodygetElementById
. - Nie jest możliwy dostęp do elementów
input
określanych przy użyciu nazw w odniesieniu do obiektuXPCNativeWrapper
opakowującego formularz HTML. Należy użyć metodyform.elements.namedItem("nazwaElementuInput")
. - Nie jest możliwy dostęp do elementów określanych przy użyciu nazw w odniesieniu do obiektu
XPCNativeWrapper
opakowującego obiektHTMLCollection
. Należy użyć metodynamedItem()
. Uwaga: metodanamedItem
zwraca jedynie pierwszy elementinput
o danej nazwie, nawet jeżeli w formularzu znajduje się więcej elementów o tej samej nazwie (np. przycisków radiowych). - Nie jest możliwe wywoływanie metod zaimplementowanych przez wtyczki NPAPI za pośrednictwem obiektu
XPCNativeWrapper
opakowującego odpowiadający im węzeł. - Nie jest możliwe ustawianie i pobieranie własności zaimplementowanych przez wtyczki NPAPI za pośrednictwem obiektu
XPCNativeWrapper
opakowującego odpowiadający im węzeł. - Nie jest możliwe wywoływanie metod zaimplementowanych poprzez wiązania XBL dołączone do węzła za pośrednictwem obiektu
XPCNativeWrapper
opakowującego ten węzeł. - Nie jest możliwe ustawianie ani pobieranie własności zaimplementowanych poprzez wiązania XBL dołączone do węzła za pośrednictwem obiektu
XPCNativeWrapper
opakowującego ten węzeł. - Przy wyliczaniu własności obiektu
XPCNativeWrapper
za pomocą pętli "for (var p in obiekt_opakowujący)
" nie są wyliczane własności zdefiniowane w specyfikacji IDL. - Własność Object.prototype nie jest częścią hierarchii prototypów obiektu
XPCNativeWrapper
. W rezultacie wiele własności obiektuObject.prototype
pozostaje niezdefiniowanych w odniesieniu do obiektuXPCNativeWrapper
(są to własności__proto__
,__parent__
,__count__
,toSource
,toLocaleString
,valueOf
,watch
,unwatch
,hasOwnProperty
,isPrototypeOf
,propertyIsEnumerable
,__defineGetter__
,__defineSetter__
,__lookupGetter__
oraz__lookupSetter__
). - Nie jest obsługiwana metoda
importXPCNative
dostępna w starszych implementacjach obiektówXPCNativeWrapper
. - Nie jest możliwy dostęp do klas standardowych (takich jak
Function
) za pośrednictwem obiektuXPCNativeWrapper
. Aby utworzyć funkcje i obiekty powiązane z rodzicem danego okna, należy skorzystać z metodyeval
tego okna.
Artykuł Avoid Common Pitfalls in Greasemonkey (Jak uniknąć typowych pułapek w Greasemonkey; tekst w jęz. ang.) zawiera obszerne omówienia niektórych z powyższych ograniczeń (w odniesieniu do skryptów Greasemonkey).