Artykuł

freeimages.com freeimages.com
cze 11 2015
0

Hakowanie strony w kontrolce WebView

Aplikacje na telefon pełnią różne funkcje, ale z założenia często są one mobilnym odpowiednikiem stron WWW. I choć w dzisiejszych czasach coraz więcej mamy w sieci stron responsywnych, to i tak komfort pracy z dedykowanym programem wciąż często bywa znacznie wyższy. Nie oznacza to oczywiście, że strony WWW na przeglądarce telefonu są czymś złym.. czasem jest to naprawdę dobra opcja na start. Okazuje się również, że dzięki RWD możliwe jest również łatwe osadzanie pewnych fragmentów witryn bezpośrednio w aplikacjach i to z naprawdę przyzwoitymi efektami.

Dobrym tego przykładem mogą być wszelkiej maści strony regulaminów, czy FAQ, które raczej nie wymagają jakiejś wielkiej interakcji ze strony użytkownika i bez problemu można je osadzić praktycznie niezauważalnie w kontrolce WebView oszczędzając sobie konieczność dublowania tego rodzaju contentu.

Z tego typu tematami jest jednak pewien problem, ponieważ w pewnym sensie możemy stracić kontrolę nad wyświetlaną treścią. W praktyce okazuje się jednak, że kontrolka WebView ma pewną ciekawą metodę, która odpowiednio użyta daje naprawdę spore możliwości, którym warto się przyjrzeć bliżej, co też dzisiaj uczynimy:-)

Przykładowa strona i użycie

Na potrzeby testów, konieczne będzie przygotowanie prostej strony WWW, która później zostanie osadzona w aplikacji. Ja swoją stronę umieściłem tutaj, a jej kod wygląda następująco:

<!DOCTYPE html>
<html lang="pl">
	<head>
		<meta charset="utf-8" />
		<title>Strona testowa</title>
        <script>
            function SayHello(userName){
            	document.getElementById("name").textContent = userName;
            }
        </script>
    </head>
    <body>
		<h1>Nagłówek</h1>
		<p class="test-paragraph">Testowy paragraf numer 1</p>
		<p class="test-paragraph">Testowy paragraf numer 2</p>
		<p id="name">Tutaj może trafić Twoje imię</p>
    </body>
</html> 

Poniżej również kod aplikacji Windows Phone, która wyświetli powyższą stronę:

<Page
    x:Class="WebViewExample.MainPage"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="using:WebViewExample"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    mc:Ignorable="d" RequestedTheme="Light"
    Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">

    <Grid x:Name="LayoutRoot">
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto"/>
            <RowDefinition Height="*"/>
        </Grid.RowDefinitions>

        <StackPanel Grid.Row="0" Margin="19,0,0,0">
            <TextBlock Text="APLIKACJA WEBVIEW" Margin="0,12,0,0"
                       Style="{ThemeResource TitleTextBlockStyle}" />
            <TextBlock Text="strona testowa" Margin="0,-6.5,0,26.5" 
                       Style="{ThemeResource HeaderTextBlockStyle}" 
                       CharacterSpacing="{ThemeResource PivotHeaderItemCharacterSpacing}"/>
        </StackPanel>

        <WebView Grid.Row="1" Margin="19,9.5,19,0" 
                 Source="http://www.altcontroldelete.pl/example/webview/index.html">
            
        </WebView>
    </Grid>
</Page>

A tak wygląda działanie aplikacji w praktyce:

Co możemy zrobić?

Kontrolkę WebView można przyrównać do czegoś na kształt ograniczonego IFrame'a, dlatego też nasze możliwości są mocno limitowane. Nasza kontrolka ma jednak jedną ciekawą metodę o nazwie InvokeScriptAsync, która w połączeniu ze zdarzeniem DOMContentLoaded, może pozwolić na wywołanie JavaScriptu umieszczonego na stronie. Spójrzmy na zmodyfikowaną deklarację kontrolki w XAMLu:

<WebView Grid.Row="1" Margin="19,9.5,19,0" 
         x:Name="WebViewControl"
         DOMContentLoaded="WebViewControl_DOMContentLoaded" 
         Source="http://www.altcontroldelete.pl/example/webview/index.html" />

A także na samą implementację event handlera dla zdarzenia DOMContentLoaded:

private async void WebViewControl_DOMContentLoaded(WebView sender, 
    WebViewDOMContentLoadedEventArgs args)
{
    await WebViewControl.InvokeScriptAsync("SayHello", new List<string>{
        "Jurek"
    });
}

Dzięki metodzie InvokeScriptAsync byliśmy w stanie wywołać funkcję SayHello, do której przekazaliśmy argument(y) w postaci listy stringów. Jak widać na poniższym screenie, mechanizm ten naprawdę działa:)

Co ciekawe, nie musimy się wcale ograniczać do kodu JavaScript umieszczonego na samej stronie, a kluczem do sukcesu jest w tym przypadku magiczna funkcja eval. Taki sam efekt moglibyśmy również uzyskać w poniższy sposób:

private async void WebViewControl_DOMContentLoaded(WebView sender, 
    WebViewDOMContentLoadedEventArgs args)
{
    await WebViewControl.InvokeScriptAsync("eval", new List<string>{
        "function SayHello2(userName){ document.getElementById('name').textContent = userName; } SayHello2('Jerzy');"
    });
}

Ten skrypt również zadziała! W praktyce bardzo często będzie mogli wywołać dowolny kod JavaScript, co pozwoli nam również na modyfikację wyglądu. Musimy jednak uważać na dwie rzeczy - poprawność semantyczną kodu oraz odpowiednie oddzielenie apostrofów i cudzysłowów (dla bezpieczeństwa warto umieścić kod modyfikujący stronę w bloku try..catch). Całe zadanie można sobie bardzo uprościć, umieszczając ewentualny skrypt w pliku Resources przypisanym dla aplikacji.

Poniższy kod zmieni tło strony, a także wielkość paragrafów oznaczonych klasą:

document.body.style.backgroundColor = '#CCC';
var paragraphs = document.getElementsByClassName('test-paragraph');
for (var i=0; i<paragraphs.length; i++) {
    paragraphs[i].style.fontSize = '30px';
}

Jak widać możliwości są naprawdę spore, ale warto również pamiętać, żeby nie przesadzać z modyfikacjami - szczególnie jeśli w aplikacji osadzamy nie swoje strony (modyfikacje mogą być źle odbierane przez autorów, lub mogą po prostu negatywnie wpłynąć na działanie określonej witryny). Zawsze lepszą opcją będzie dostarczenie odpowiedniej funkcjonalności wewnątrz aplikacji.

Warto również wspomnieć, że możliwa jest również komunikacja w drugą stronę za pomocą zdarzenia ScriptNotify. Więcej na ten temat znajdziecie w tym artykule.

Podoba Ci się ten wpis? Powiedz o tym innym!

Send to Kindle

Komentarze

blog comments powered by Disqus